template<class T>
void func(T k) {
T x(k);
x.do_something();
}
noexcept 紧跟在函数的参数列表后面,它只用来表明两种状态:“不抛异常” 和 “抛异常”
void func_not_throw() noexcept; // 保证不抛出异常
void func_not_throw() noexcept(true); // 保证不抛出异常
void func_throw() noexcept(false); // 可能会抛出异常
void func_throw(); // 可能会抛出异常,若不显示说明,默认是会抛出异常(除了析构函数,详见下面)
对于一个函数而言,
需要注意的是,编译器不会检查带有 noexcept 说明符的函数是否有 throw。
void func_not_throw() noexcept {
throw 1; // 编译通过,不会报错(可能会有警告)
}
程序会直接调用 std::terminate,并且不会栈展开(Stack Unwinding)(也可能会调用或部分调用,取决于编译器的实现)。另外,即使你有使用 try-catch,也无法捕获这个异常。
#include
using namespace std;
void func_not_throw() noexcept {
throw 1;
}
int main() {
try {
func_not_throw(); // 直接 terminate,不会被 catch
} catch (int) {
cout << "catch int" << endl;
}
return 0;
}
noexcept 除了可以用作说明符(Specifier),也可以用作运算符(Operator)。noexcept 运算符是一个一元运算符,它的返回值是一个 bool 类型的右值常量表达式,用于表示给定的表达式是否会抛出异常。
void f() noexcept {
}
void g() noexcept(noexcept(f)) { // g() 是否是 noexcept 取决于 f()
f();
}
其中 noexcept(f) 返回 true,则上式就相当于 void g() noexcept(true)。
析构函数默认都是 noexcept 的。C++ 11 标准规定,类的析构函数都是 noexcept 的,除非显示指定为 noexcept(false)。
class A {
public:
A() {}
~A() {} // 默认不抛出异常
};
class B {
public:
B() {}
~B() noexcept(false) {} // 可能会抛出异常
};
在为某个异常进行栈展开的时候,会依次调用当前作用域下每个局部对象的析构函数,如果这个时候析构函数又抛出自己的未经处理的另一个异常,将会导致 std::terminate。所以析构函数应该从不抛出异常。
调用 noexcept 函数时不需要记录 exception handler,所以编译器可以生成更高效的二进制码(编译器是否优化不一定,但理论上 noexcept 给了编译器更多优化的机会)。另外编译器在编译一个 noexcept(false) 的函数时可能会生成很多冗余的代码,这些代码虽然只在出错的时候执行,但还是会对 Instruction Cache 造成影响,进而影响程序整体的性能。
一个 std::vector,若要进行 reserve 操作,一个可能的情况是,需要重新分配内存,并把之前原有的数据拷贝(copy)过去,但如果 T 的移动构造函数是 noexcept 的,则可以移动(move)过去,大大地提高了效率。
#include
#include
using namespace std;
class A {
public:
A(int value) {
}
A(const A &other) {
std::cout << "copy constructor\n";
}
A(A &&other) noexcept {
std::cout << "move constructor\n";
}
};
int main() {
std::vector<A> a;
a.emplace_back(1);
a.emplace_back(2);
return 0;
}
把移动构造函数的 noexcept 说明符去掉,则会输出:
这不用多说,必须也应该为 noexcept。
尽量让上面的函数都是 noexcept,这可能会给你的代码带来一定的运行期执行效率。
比如像是 int,pointer 这类的 getter,setter 都可以用 noexcept。因为不可能出错。也可以看下准标准库 Boost 的源码,全局搜索BOOST_NOEXCEPT。