C++ 特殊成员函数是否为noexcept或throw()?
C++11规范明确指出,隐式生成的特殊函数(即默认构造函数、析构函数、复制/移动构造函数和复制/移动赋值运算符)具有异常规范。但该规范似乎只根据现在不推荐的动态异常规范(即“throw(T1,T2,T3)”)编写。15.4/14中的示例支持这一点:C++ 特殊成员函数是否为noexcept或throw()?,c++,c++11,C++,C++11,C++11规范明确指出,隐式生成的特殊函数(即默认构造函数、析构函数、复制/移动构造函数和复制/移动赋值运算符)具有异常规范。但该规范似乎只根据现在不推荐的动态异常规范(即“throw(T1,T2,T3)”)编写。15.4/14中的示例支持这一点: struct A { A(); A(const A&) throw(); A(A&&) throw(); ~A() throw(X); }; struct B { B() throw(); B(cons
struct A {
A();
A(const A&) throw();
A(A&&) throw();
~A() throw(X);
};
struct B {
B() throw();
B(const B&) throw();
B(B&&) throw(Y);
~B() throw(Y);
};
struct D : public A, public B {
// Implicit declaration of D::D();
// Implicit declaration of D::D(const D&) throw();
// Implicit declaration of D::D(D&&) throw(Y);
// Implicit declaration of D::D() throw(X, Y);
};
我知道注释不是规范性的,但值得注意的是,D的复制构造函数声明为
throw()
,而不是noexcept
。这是有区别的,因为违反throw()
与违反noexcept
时程序的行为是不同的
上文第15.4/14节中的文本是规范性的,它说:
隐式声明的特殊成员函数(第12条)
应具有例外规范。如果f是隐式声明的
默认构造函数、复制构造函数、移动构造函数、析构函数、,
复制赋值运算符或移动赋值运算符的隐式
异常规范指定类型id T的当且仅当T为
由直接调用的函数的异常规范所允许
由f的隐式定义;f应允许所有例外情况(如有)
它直接调用的函数允许所有异常,f应允许
如果它直接调用的每个函数都不允许
例外情况
考虑到这里只引用动态异常规范,我担心隐式生成的特殊成员函数永远不会声明
noexcept
。真的是这样吗?我不相信是这样。实际要求只是“…f应不允许任何异常,如果它直接调用的每个函数都不允许任何异常。”
如上所述(§15.4/12):
如果异常规范的格式为throw()
,noexcept
,或noexcept(
constant expression)
,其中常量表达式产生true
,则该异常规范为非抛出。具有非引发异常规范的函数不允许任何异常
这并不是说非抛出异常规范不允许任何异常,但我认为这说明了非抛出异常规范描述中的措辞在隐式声明的特殊成员函数的需求中几乎一字不差地得到响应。因此,在我看来,任何形式的非抛出异常规范(
throw()
或noexcept
或noexcept()
)都是允许的——而且这是专门设计的,而不仅仅是措词上的意外。它们怎么可能被声明为noexcept
?您是否希望编译器检查您的代码并检查您是否从未抛出异常?@SethCarnegie:如果编译器推断动态异常规范为throw()
,我希望编译器使用noexcept
。因为“12[…]具有非抛出异常规范的函数不允许任何异常。”,不管是noexcept
还是throw()
都是一个有效的异常规范,不允许任何异常。这对于第14段来说似乎已经足够了,它并不特别需要动态异常规范。我不确定我是否掌握了全部情况,是否能够回答这个问题。“这很重要,因为违反throw()和违反noexcept时,程序的行为是不同的。”真的吗?这是怎么回事?@Nemo程序的行为在违反throw()
与noexcept
的情况下有所不同,即如果实际抛出异常noexcept
调用std::terminate
,在这种情况下,可能没有完全展开堆栈。我可以看到如何以这种方式读取它,但考虑到noexcept
和throw()
之间的语义差异,如果标准能更精确地表达函数不抛出的概念,那就更好了。