在正式进入正题之前我们需要了解一点别的知识: 函数签名(function signature).
具体请参阅这里: http://blog.csdn.net/weiwangchao_/article/details/7165467
- 自从C++11起提供了关键字noexcept,用来指明某个函数无法或不打算抛出异常.
另外需要注意的是noexcept(expression)中:
expression的值必须是编译时就得到的(during compling).
例如:
void foo()noexcept;
声明了foo()不打算抛出异常。
但是如果foo()内有异常未被处理,也就是说在我们指定了noexcept的情况下foo()仍然抛出了异常,程序会被终止,然后std::terminal()会调用std::abort().(调用std::abort()也就带来了一个问题,会立刻终止该函数,停止程序,而不做任何清理).
还有需要注意的是noexcept,不会造成stack unwinding(栈展开),也就是说可以表现不抛出异常而不需要额外的开销.
我们再开看一个例子:
像 void foo() noexcept中的noexcept,其实相当于noexcept(true).也就是说我们可以指定一个条件。如果noexcept(false)那么表明有抛出异常的可能。
pair& operator=(pair&& p)noexcept(std::is_nothrow_move_assignable<T1>::value &&
std::is_nothrow_move_assginable<T2>::value);
上面的例子中用到了#include<type_traits>中的std::is_nothrow_move_assignable(用来检查针对传入的类型时候存在一个不抛出异常的移动赋值运算符(move-assgin)
demo1(可以编译通过):
#include <iostream>
#include <exception>
void func1(const int& number)
{
throw std::runtime_error{ "funct1 error!" };
}
int main()
{
func1(10);
return 0;
}
demo2(会产生编译警告,错误的写法实际中不要这么用):
#include <iostream>
#include <exception>
void func1(const int& number)noexcept
{
throw std::runtime_error{ "funct1 error!" };
}
int main()
{
func1(10);
return 0;
}
demo3(编译通过,但需要特别注意的是,这也是错误的):
#include <iostream>
#include <exception>
void func1(const int& number)noexcept //noexcept(true)
{
try {
throw std::runtime_error{ "funct1 error!" };
}catch (const std::runtime_error& error) {
std::cerr << error.what() << std::endl;
}
std::cout << "<---------------------->" << std::endl;
}
int main()
{
func1(10);
return 0;
}
- 从C++17开始noexcept成为了函数类型(function type)的一部分,但它不是函数签名(function signature)的一部分.
demo1:
//尽管异常级别不同,但是由于noexcept只作为function type的一部分,比不能用来overload.
//就像函数返回类型(function return type)一样,也不能用来作为重载的依据.
void f() noexcept;
void f(); // error: different exception specification
demo2:
void ft(); // potentially-throwing
void (*fn)() noexcept = ft; // error
demo3: 针对虚函数
struct B {
virtual void f() noexcept;
virtual void g();
virtual void h() noexcept = delete;
};
struct D: B {
void f(); // ill-formed: D::f is potentially-throwing, B::f is non-throwing
void g() noexcept; // OK
void h() = delete; // OK
};
- noexcept从C++17开始除了可以指定一个函数不throw异常外(相当于C++11之前的throw()),还提供了限制异常的扩散.具体如何限制呢?
demo1:
#include <iostream>
using namespace std;
void Throw() { throw 1; }
void NoBlockThrow() { Throw(); }
void BlockThrow() noexcept { Throw(); }//注意指定了noexcept
void BlockThrowNoException()noexcept { throw 1; }
int main()
{
//case 1: 能够catch到.
try {
Throw();
}
catch (...) {
cout << "Found throw." << endl;
}
//case 2: 能够catch到.
try {
NoBlockThrow();
}
catch (...) {
cout << "Throw is not blocked." << endl;
}
//case 3: 不能catch到.
try {
BlockThrow();
}
catch (...) {
cout << "Found throw 1." << endl;
}
//case 4: 不能catch到.
try {
BlockThrowNoException();
}
catch (...) {
cout << "Found throw 1." << endl;
}
return 0;
}
- 而同样出于安全考虑,自C++11标准中让类的析构函数默认也是noexcept(true)的(无论是我们自己定义的还是default)。当然,如果显式地为析构函数指定了noexcept(false)或者类的基类或成员有noexcept(false)的析构函数,析构函数就不会再保持默认值。
demo1:所有成员函数均为default
输出均为: true
#include <iostream>
class Test1
{
public:
//Test1() {}
//Test1(const Test1&) noexcept(false)/*= default;*/ {}
//Test1(Test1&&) noexcept(false) {}
//Test1& operator=(const Test1&) = default;
//Test1& operator=(Test1&&) = default;
//~Test1() = default;
};
int main()
{
Test1 test1;
std::cout << std::boolalpha;
//输出均为: true;
std::cout << noexcept(Test1{}) << std::endl
<< noexcept(std::declval<Test1>().~Test1()) << std::endl
<< noexcept(Test1{ std::declval<Test1>() }) << std::endl
<< noexcept(Test1{ test1 }) << std::endl
<< noexcept(Test1{}.operator=(test1)) << std::endl
<< noexcept(Test1{}.operator=(std::declval<Test1>())) << std::endl;
return 0;
}
demo2: 自定义析构函数(destroy)
产生编译警告,且无法catch到exception.
#include <iostream>
class Test1
{
public:
~Test1()
{
throw 1;
}
};
int main()
{
try {
Test1 test;
}
catch (...)
{
std::cout << "Catched the exception!" << std::endl;
}
return 0;
}
demo3: 自定义构造函数(constructor),但是需要注意的是copy/move-constructor, copy/move-operator=都是default的!
#include <iostream>
class Test1
{
public:
Test1() {}
//Test1(const Test1&) noexcept(false)/*= default;*/ {}
//Test1(Test1&&) noexcept(false) {}
//Test1& operator=(const Test1&) = default;
//Test1& operator=(Test1&&) = default;
//~Test1() = default;
};
int main()
{
Test1 test1;
std::cout << std::boolalpha;
std::cout << noexcept(Test1{}) << std::endl //false
<< noexcept(Test1{ std::declval<Test1>() }) << std::endl
<< noexcept(Test1{ test1 }) << std::endl
<< noexcept(Test1{}.operator=(std::declval<Test1>())) << std::endl //false
<< noexcept(Test1{}.operator=(test1)) << std::endl //false
<< noexcept(std::declval<Test1>().~Test1()) << std::endl;
return 0;
}
demo4:成员的析构函数potentially-throw
#include <iostream>
struct Test
{
~Test()noexcept(false) {}
};
class Test1
{
private:
Test t;
public:
//Test1() {}
//Test1(const Test1&) noexcept(false)/*= default;*/ {}
//Test1(Test1&&) noexcept(false) {}
//Test1& operator=(const Test1&) = default;
//Test1& operator=(Test1&&) = default;
~Test1() = default;
};
int main()
{
Test1 test1;
std::cout << std::boolalpha;
std::cout << noexcept(Test1{}) << std::endl //false
<< noexcept(Test1{ std::declval<Test1>() }) << std::endl
<< noexcept(Test1{ test1 }) << std::endl
<< noexcept(Test1{}.operator=(std::declval<Test1>())) << std::endl //false
<< noexcept(Test1{}.operator=(test1)) << std::endl //false
<< noexcept(std::declval<Test1>().~Test1()) << std::endl; //false
return 0;
}
- template with noexcept operator
函数模板(function template)在声明(declare)中如果带有noexcept operator,在声明这个函数的时候是不会实例化(instantiate)异常级别的(当然可以通过特例化来完成强行异常指定),只有在需要(in needed)的时候才会.
demo 1:
#include <iostream>
struct A{
};
struct B{
int x;
};
template<typename T>
void func()noexcept(sizeof(T) >= 4)
{
std::cout << "test" << std::endl;
}
typedef void (*forA)()noexcept(false);
typedef void (*forB)()noexcept(true);
int main()
{
forA a = func<A>;
forB b = func<B>;
return 0;
}