C++17之noexcept作为类型的一部分 在c 17异常处理规范成为函数类型的一部分。也就是说下面两个函数现在有两种不同的类型:void f1(); void f2() noexcept; // different type在c 17之前这两个函数都具有相同的类型。因此编译器现在将检测如果你使用一个函数抛出异常而一个函数不抛出任何异常的情况:void (*fp)() noexcept; // pointer to function that doesn’t throw fp f2; // OK fp f1; // ERROR since C17当然在允许抛出函数的地方使用不抛出的函数仍然是有效的:void (*fp2)(); // pointer to function that might throw fp2 f2; // OK fp2 f1; // OK因此这个新特性不会破坏那些还没有使用noexcept函数指针的程序但是现在可以确保您不再违反函数指针中的noexcept规范。但是不允许重载具有不同异常规范的同一签名的函数名(因为只允许重载具有不同返回类型的函数):void f3(); void f3() noexcept; // ERROR注意所有其他规则都不受影响。例如仍然不允许忽略基类的noexcept规范:class Base { public: virtual void foo() noexcept; ... }; class Derived : public Base { public: void foo() override; // ERROR: does not override ... };在这里派生类中的成员函数foo()具有不同的类型因此它不会覆盖基类的foo()。这段代码仍然不能编译。即使没有覆盖说明符这段代码也无法编译因为我们仍然不能使用更松散的抛出规范进行重载。1. 使用条件异常规范当使用条件noexcept规范时函数的类型取决于条件是真还是假:void f1(); void f2() noexcept; void f3() noexcept(sizeof(int)4); // same type as either f1() or f2() void f4() noexcept(sizeof(int)4); // different type than f3()这里f3()的类型取决于编译代码时条件的类型:如果sizeof(int)等于4(或大于4)则函数签名为void f3() noexcept(false);//与f1()类型相同如果sizeof(int)小于4则函数签名为void f3() noexcept(true); // same type as f2()因为f4()的异常条件使用了f3()的相反表达式所以f4()总是有不同的类型(即它保证在f3()不抛出时抛出反之亦然。以前的throw规范仍然可以使用但自从c 17以来就被弃用了:void f5() throw(); // same as void f5() noexcept but deprecated不再支持动态抛出规范(自c 11以来一直不支持):void f6() throw(std::bad_alloc); // ERROR: invalid since C172. 对通用库的影响对于泛型库只使用类型的except声明部分可能会产生一些后果。例如下面的程序在c 14之前是有效的但是不再用c 17编译:#include iostream templatetypename T void call(T op1, T op2) { op1(); op2(); } void f1() { std::cout f1()\n; } void f2() noexcept { std::cout f2()\n; } int main() { call(f1, f2); // ERROR since C17 }问题是由于c 17 f1()和f2()具有不同的类型所以编译器在实例化函数模板调用()时不再为这两种类型找到一个公共类型T。在c 17你必须使用两种不同的类型:templatetypename T1, typename T2 void call(T1 op1, T2 op2) { op1(); op2(); }如果希望或必须重载所有可能的函数类型那么现在必须加倍重载。例如这适用于标准类型trait std::is_function的定义。定义了主模板因此通常类型T没有函数:// primary template (in general type T is no function): templatetypename T struct is_function : std::false_type { };模板派生自std::false_type因此is_functionT::value对于任何类型的T通常都会产生false。对于所有属于函数的类型都存在部分专门化它们派生自std::true_type使成员值为true:// partial specializations for all function types:templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) : std::true_type { };templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) const : std::true_type { };templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) : std::true_type { };templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) const : std::true_type { };......在c 17之前已经有24个局部专门化因为函数类型可以有const和volatile限定符也可以有lvalue()和rvalue()引用限定符而且需要重载带有可变参数列表的函数。在c 17中通过在所有这些局部专门化中添加noexcept限定符局部专门化的数量增加了一倍现在我们得到了48个局部专门化:...// partial specializations for all function types with noexcept:templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) noexcept : std::true_type { };templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) const noexcept : std::true_type { };templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) noexcept : std::true_type { };templatetypename Ret, typename... Paramsstruct is_functionRet (Params...) const noexcept : std::true_type { };...没有实现noexcept重载的库可能不再编译代码这些代码使用它们将函数或函数指针传递到不需要noexcept的地方。