三个不同的编译器显示编译此代码的三种不同行为:
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补丁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();
}
我知道问题是什么以及我如何解决它:
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;
};
但是我想知道标准在具体情况下说了什么。
我在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;
}
这两个功能的签名是不同的。这怎么办?似乎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;
}
这是c ++标准吗?是否有任何神奇的旗帜可以设置?
VS2008和VS2010怎么样?
答案 0 :(得分: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
};
D::f
的声明格式不正确,因为它允许所有例外,而B::f
仅允许int
和double
。 ]
答案 1 :(得分:5)
它在C ++ 11中发展了一点 [except.spec] :
5 / 如果虚函数有异常规范,那么在任何派生类中覆盖该虚函数的任何函数的所有声明(包括定义)都只允许允许的异常。基类虚函数的异常规范。
因此,您实际上从未被允许指定更宽松的异常规范。
然而,这种情况很棘手,因为析构函数实际上是由编译器本身合成的!
在C ++ 03中,我认为标准对这些并不那么谨慎,你必须自己编写,在C ++ 11中我们得到:
14 / 隐式声明的特殊成员函数(第12条)应具有异常规范。如果
f
是隐式声明的默认构造函数,复制构造函数,移动构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常规范指定type-idT
if和only 如果由T
的隐式定义直接调用的函数的异常规范允许f
;如果它直接调用的任何函数允许所有异常,f
将允许所有异常,并且f
如果它直接调用的每个函数都不允许异常,则不允许异常。
编译器将生成析构函数的异常规范,以便它匹配从它调用的函数(即属性的析构函数)抛出的内容。如果这些析构函数不抛出,那么它将生成一个noexcept
析构函数,它将满足基类约束。
注意:VS2005是您可能在地球上找到的最不符合标准的编译器之一。