内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用
三种不同的编译器显示了三种不同的编译代码的行为:
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; };
Sun C++ 5.8 Patch 121017-22 2010/09/29: Warning Function MyException::~MyException() can throw only the exceptions thrown by the function std::exception::~exception() it overrides
g++ 3.4.3: Error looser throw specifier for `virtual MyException::~MyException()'
VisualStudio 2005:(既无错误也无警告)
class exception { public: exception () throw(); exception (const exception&) throw(); exception& operator= (const exception&) throw(); virtual ~exception() throw(); virtual const char* what() const throw(); }
我知道问题所在,也知道如何解决问题:
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; };
然而,我想知道标准在特定情况下说了些什么。
我在VisualStudio 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; }
这两个函数的签名是不同的。这怎么行?Visualstudio 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; }
这是c++标准吗?
VS 2008和VS 2010怎么办?
它在C++11中有所发展。[except.spec]::
[5]如果一个虚拟函数具有异常规范,则在任何派生类中覆盖该虚拟函数的所有声明,包括定义,都只能允许基类虚拟函数的异常规范所允许的异常。
因此,永远不允许实际指定looser异常规范
但是,这种情况很棘手,因为析构函数实际上是由编译器本身合成的!
在C++03中,我认为标准对这些问题没有那么仔细,你必须自己编写,在C++11中,但是我们得到:
[14]隐式声明的特殊成员函数(第12条)应具有例外说明。如果
f
是隐式声明的默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值运算符或移动赋值运算符,其隐式异常规范指定类型id。T
当且仅当T
直接调用的函数的异常规范所允许的f
含蓄定义;f
如果它直接调用的任何函数允许所有异常,则应允许所有异常,以及f
如果它直接调用的每个函数都不允许异常,则不允许异常。
编译器将生成析构函数的异常规范,使其与从其调用的函数(即属性的析构函数)中抛出的函数匹配。如果这些析构函数不抛出,那么它将生成一个noexcept
将满足基类约束的析构函数。