noexcept运算符
noexcept说明符的实参常常与noexcept运算符(noexcept operator)混合使用。noexcept运算符是一个一元运算符,它的返回值是一个bool类型的右值常量表达式,用于表示给定的表达式是否会抛出异常。和sizeof类似,noexcept也不会求其运算对象的值。
例如,因为我们 声明recoup时使用了noexcept说明符,所以下面的表达式的返回值为true。
noexcept(recoup(i)); //如果recoup不抛出异常则结果为true;否则结果为false
更普通的形式是:
noexcept(e);
当e调用的所有函数都做了不抛出异常说明且e本身不含有trhow语句时,上述表达式为true;否则noexcept(e)返回false。
我们可以使用except说明符得到如下的异常说明:
void f() noexcept(noexcept(g())); //f和g的异常说明一致
如果g承诺了不会抛出异常,则f也不会抛出异常;如果g没有异常说明符,或者g虽然有异常说明符但是允许抛出异常,则f也可能抛出异常。
异常说明与指针、虚函数和拷贝控制
尽管noexcept说明符不属于函数类型的一部分,但是函数的异常说明符会影响函数的使用。
函数指针及该指针所指函数必须具有一致的的异常说明。也就是说,如果我们为某个指针做了不抛出异常说明,则该指针只能指向不抛出异常函数。相反,如果我们显示或殷实地指明了指针可能抛出异常,则该指针可以指向任意函数,即使是承诺了不抛出异常的函数也可以:
//recoup和pf1都承诺不会抛出异常 void (*pf1)(int) noexcept = recoup; //正确:recoup不会抛出异常,pf2可能抛出异常,二者之间互不干扰 void (*pf2)(int) = recoup; pf1 = alloc; //错误:alloc可能抛出异常,但是pf1已经说明了它不会抛出异常 pf2 = alloc; //正确:pf2和alloc都可能抛出异常
如果一个虚函数承诺了它不会抛出异常,则后续派生出来的虚函数也必须做出同样的承诺;与之相反,如果基类的虚函数允许抛出异常,则派生类的对应函数既可以抛出异常,也可以不抛出异常:
class Base { public: virtual double f1(double) noexcept; //不会抛出异常 virtual int f2() noexcept(false); //不会抛出异常 virtual void f3(); //可能抛出异常 }; class Derived : public Base { public: double f1(double); //错误:Base::f1承诺不会抛出异常 int f2() noexcept(false); //正确:与Base::f2的异常说明一致 void f3() noexcept; //正确:Derived的f3做了更严格的规定 //这是允许的 };
当编译器合成拷贝控制成员时,同时也会生成一个异常说明。如果对所有成员和基类的所有操作都承诺不会抛出异常,则合成的成员是noexcept的。如果合成成员调用的任何一个函数可能抛出异常,则合成成员是noexcept(false)。而且,如果我们定义了析构函数但是没有为它提供异常说明,则编译器将合成一个。合成的异常说明将与假设由编译器为类合成析构函数时所得的异常说明一致。