关于在默认析构函数中丢失throw说明符的c ++标准所说的内容

Ale*_*zzi 19 c++ visual-c++

三个不同的编译器显示编译此代码的三种不同行为:

class MyException : public std::exception
{
 public:
  MyException(std::string str) : m_str(str) {}
  virtual const char * what() const throw () {return m_str.c_str(); }
 protected:
  std::string m_str;
};
Run Code Online (Sandbox Code Playgroud)

Sun C++ 5.8 Patch 121017-22 2010/09/29:警告函数MyException :: 〜MyException ()只能抛出函数std :: exception :: ~exception()抛出的异常它会覆盖

g ++ 3.4.3: `virtual MyException:~MyException()'的错误抛出说明符

Visual Studio 2005:非常高兴(既不是错误也不是警告)

class exception {
public:
  exception () throw();
  exception (const exception&) throw();
  exception& operator= (const exception&) throw();
  virtual ~exception() throw();
  virtual const char* what() const throw();
}
Run Code Online (Sandbox Code Playgroud)

我知道问题是什么以及如何解决它:

class MyException : public std::exception
{
 public:
  MyException(std::string str) : m_str(str) {}
  virtual const char * what() const throw () {return m_str.c_str(); }
  ~MyException() throw() {}  <------------ now it is fine!
 protected:
  std::string m_str;
};
Run Code Online (Sandbox Code Playgroud)

但是我想知道标准在具体情况下说了什么.

我在Visual Studio 2005中运行了另一个小测试,我发现了一些令我惊讶的东西:

struct Base
{
    virtual int foo() const throw() { return 5; }
};

struct Derived : public Base
{
    int foo() const { return 6; }
};

int main()
{
    Base* b = new Derived;
    std::cout << b->foo() << std::endl; //<-- this line print 6!!!
    delete b;
}
Run Code Online (Sandbox Code Playgroud)

这两个功能的签名是不同的.这怎么办?似乎visual studio 2005完全忽略了异常规范.

struct Base
{
    virtual int foo() const throw() { return 5; }
};

struct Derived : public Base
{
    int foo() { return 6; } // I have removed the const keyword 
                            // and the signature has changed
};

int main()
{
    Base* b = new Derived;
    std::cout << b->foo() << std::endl; // <-- this line print 5
    delete b;
}
Run Code Online (Sandbox Code Playgroud)

这是c ++标准吗?有没有神奇的旗帜可以设定?

VS2008和VS2010怎么样?

Alo*_*ave 6

根据C++标准,您的程序格式不正确,因此演示了一种无法在C++标准范围内解释的行为.

参考:
C++ 03标准:

15.4例外规范[except.spec]

如果虚函数具有异常规范,则在任何派生类中覆盖该虚函数的任何函数的所有声明(包括定义)都应仅允许基类虚函数的异常规范所允许的异常.

[例:

 struct B 
 {
    virtual void f() throw (int, double);
    virtual void g();
 };
 struct D: B 
 {
    void f(); // ill-formed
    void g() throw (int); // OK
 };
Run Code Online (Sandbox Code Playgroud)

声明D::f 是不正确的,因为它允许所有例外,而B::f只允许intdouble.]


Mat*_* M. 5

它在C++ 11 [except.spec]中演变了一点:

5 /如果虚函数具有异常规范,则任何在任何派生类中覆盖该虚函数的函数的所有声明(包括定义)都只允许基类虚函数的异常规范所允许的异常.

因此,您实际上从未被允许指定更宽松的异常规范.

但是这种情况很棘手,因为析构函数实际上是由编译器本身合成的!

在C++ 03中,我认为标准对这些并不那么谨慎,你必须自己编写它们,在C++ 11中我们得到:

14 /隐式声明的特殊成员函数(第12条)应具有异常规范.如果f是隐式声明的默认构造函数,复制构造函数,移动构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常规范指定type-id T当且仅当T函数的异常规范允许时才由f隐式定义调用; f如果它直接调用的任何函数允许所有异常,则允许所有异常,并且f如果它直接调用的每个函数都不允许异常,则不允许异常.

编译器将生成析构函数的异常规范,以便它匹配从它调用的函数抛出的内容(即属性的析构函数).如果这些析构函数不抛出,那么它将生成一个noexcept满足基类约束的析构函数.

注意:VS2005是您可能在地球上找到的最不符合标准的编译器之一.