C++11之前我们可以使用throw抛出异常,但是随着C++11中移动语义的产生,throw不能很好的解决移动语义过程中的异常处理,不同于拷贝,移动的过程如果出错的话不能保证原来的数据是否受到影响,为此C++11引入了noexcept关键字来处理这个问题。
noexcept作为说明符
函数使用noexcept声明时,会告诉编译器这个函数不会发生异常,进而让编译器使用效率更高的检测代码,如果真的发生异常时程序会调用terminate函数直接结束程序。
noexcept还可以接收一个bool类型的参数,该参数必须是一个常量表达式用以决定函数是否可以抛出异常,默认是true
//不带参数
void fun()noexcept{}
//带参数
template <class T>
T fun()noexcept(std::is_fundamental<T>::value){}
noexcept作为运算符
作为运算符接收表达式参数返回bool类型,判断表达式是否可以抛出异常
void fun()noexcept{}
void fun(int){}
void fun(char)throw(){}
int main(){
std::cout<<noexcept(fun())
<<noexcept(fun(1))
<<noexcept(fun('a'))<<std::endl;
}
//分别返回 true false true
解决移动语义的风险
- 遇到风险直接编译报错
template<class T>
void swap(T& a,T& b)
noexcept(noexcept(T(std::move(a))))&&
noexcept(noexcept(T(std::move(b))))
{
static_assert(noexcept(T(std::move(a)))&&
noexcept(T(std::move(b))));
T tmp(std::move(a));
a=std::move(b);
b=std::move(tmp);
}
- 遇到风险改用拷贝
template<class T>
void swap_impl(T& a,T& b,std::integral_constant<bool,true>)noexcept{
T tmp(std::move(a));
a=std::move(b);
b=std::move(tmp);
}
template<class T>
void swap_impl(T& a,T& b,std::integral_constant<bool,false>){
T tmp(a);
a=b;
b=tmp;
}
template<class T>
void swap(T& a,T& b)
noexcept(noexcept(swap_impl(a,b
std::integral_constant<bool,noexcept(T(std::move(a)))&&
noexcept(a.operator=(std::move(b)))>())))
{
swap_impl(a,b,
std::integral_constant<bool,noexcept(T(std::move(a)))&&
noexcept(a.operator=(std::move(b)))>());
}
noexcept和throw()
- C++11:声明函数的结果相同,noexcept更高效
- C++17:throw变为noexcept的别名
- C++20:废除throw
默认声明noexcept的函数
- 默认构造函数
- 默认拷贝构造
- 默认移动构造
- 默认赋值函数
- 默认移动赋值函数
- 析构函数
- delete运算符函数
使用noexcept的情景:
- 绝对不会发生错误的函数
- 绝对不能发生错误的函数
C++17之前noexcept并不作为函数类型的一部分,下列代码中foo并不具备noexcept声明为fp赋值后fp会失去noexcept的声明效果
void(*fp)()noexcept=nullptr;
void foo(){}
int main(){
fp=&foo;
}