TheRiver | blog

You have reached the world's edge, none but devils play past here

0%

noexcept

参考

C++11:noexcept异常说明

effective modern c++

wikipedia

stackoverflow

wiki

事实上,异常规格(throw这一特性在程序中很少被使用,因此在C++11中被弃用[2]。C++11定义了新的noexcept关键字。如果在函数声明后直接加上noexcept关键字,表示函数不会抛出异常。另外一种形式是noexcept关键字后跟常量表达式,其值转为布尔值,如果为真表示函数不会抛出异常,反之,则有可能抛出异常。

returnType funcDeclaration (args) noexcept(常量表达式) ;

如果保证不抛出异常的函数却实际上抛出异常,则会直接调用std::terminate中断程序的执行。

noexcept关键字还可以用作运算符,其后的操作数表达式如果有可能抛出异常,则运算符返回为false;如果操作数表达式保证不抛出异常,则运算符返回为true。这一运算符用于在定义模板函数时可以根据模板参数类型来确定是否传出异常。

对类析构函数,使用noexcept关键字也可以显式指明不剖出异常。类析构函数默认不抛出异常。如果声明为(或默认)不抛出异常的类析构函数在运行时抛出了异常,将导致调用std::terminate中断程序的执行。

以下两种等价:

int f(x) throw();       //c++98
int f(x) noexcept();    //c++11

性能上,noexcept可以让编译器做更多的优化,因此:

int f(x) noexcept();    //最优化
int f(x) throw();       //优化不够
int f(x);               //优化不够

move_if_noexcept

stl容器比如vector拥有强异常安全保证,比如vector重新调整大小的时候:

  • 分配原来两倍的大小
  • 拷贝原来的数据到新的空间
  • 析构原来的数据

如果拷贝的过程出现异常,还可以还原到拷贝前的状态,放弃拷贝

c++11后有了移动操作,对于复制数据来说提升了性能,但可能导致强异常安全保证失效,因此只有在确保Move构造函数不会导致异常的情况下,容易才优先采用移动赋值函数而不是拷贝赋值函数,这里用到了move_if_noexcept

stackoverflow:

move_if_noexcept: move_if_move_ctor_is_noexcept_or_the_only_option

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct A {
A ();
A (A const&);
};

A a1;
A a2 (std::move_if_noexcept (a1)); // `A const&` => copy-constructor
struct B {
B ();
B (B const&);
B (B&&) noexcept;
};

B b1;
B b2 (std::move_if_noexcept (b1)); // `B&&` => move-constructor
// ^ it's `noexcept`
struct C {
C ();
C (C&&);
};

C c1;
C c2 (std::move_if_noexcept (c1)); // `C&&` => move-constructor
// ^ the only viable alternative
struct D {
C ();
C (C const&) noexcept;
};

C c1;
C c2 (std::move_if_noexcept (c1)); // C&& => copy-constructor
// ^ can be invoked with `T&&`

值得注意的地方是move_if_noexcept是针对于构造函数而言的,判断拷贝构造函数和移动构造函数的存在以及异常性来决定的。

测试下vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <iostream>
#include <vector>

using namespace std;

class t_no{
public:
t_no() = default;
t_no(const t_no &){
cout << "copy constructor" << endl;
}

t_no(t_no &&) noexcept
{
cout << "move constructor" << endl;
}
};

class t{
public:
t() = default;
t(const t &){
cout << "copy constructor" << endl;
}

t(t &&)
{
cout << "move constructor" << endl;
}
};

int main()
{
vector<t_no> v1;
vector<t> v2;

for (int i = 0; i < 3; i++)
{
t_no a;
v1.push_back(a);
cout << v1.capacity() << endl;
cout << "-------------------" << endl;
}

for (int i = 0; i < 3; i++)
{
t a;
v2.push_back(a);
cout << v2.capacity() << endl;
cout << "-------------------" << endl;
}


}

#if 0
copy constructor
1
-------------------
copy constructor
move constructor
2
-------------------
copy constructor
move constructor
move constructor
4
-------------------
copy constructor
1
-------------------
copy constructor
copy constructor
2
-------------------
copy constructor
copy constructor
copy constructor
4
-------------------

#endif

swap中对于底层对象noexcept的要求:

todo

总结

  • 析构函数,内存释放释放默认都是noexcept的,除非显示指定noexcept(false)

ending

2020071205.jpg

----------- ending -----------