C++11 noexcept

本文详细介绍了C++11引入的noexcept特性,包括noexcept指定符和noexcept运算符的使用方法,以及它们在C++17及之前的语法差异。通过实例展示了如何判断函数是否可能抛出异常,以及如何在函数模板中应用noexcept。

C++11 引入了noexcept,它有两类作用:noexcept 指定符和noexcept 运算符

一 noexcept 指定符

1 含义:指定函数是否抛出异常。

2 两类语法:

noexcept    (1)  同noexcept(true)
noexcept(expression)    (2)

3 解释

C++17起: 规定noexcept是函数类型的一部分,并可以作为函数声明器的一部分出现。
C++17前:
noexcept 规定不是函数类型的一部分(正如同动态异常规定),而且只能在声明函数、变量、函数类型的非静态数据成员、指向函数指针、到函数的引用或指向成员函数的指针时,和声明类型正好是指向函数指针或到函数的引用的参数或返回类型时,作为 lambda 声明器或顶层函数声明器的一部分出现。它不能出现于 typedef 或类型别名声明。

 其中:"动态异常规定":列出函数可能直接或间接抛出的异常。在C++11中弃用。

4 举例

void f() noexcept; // 函数 f() 不抛出
void (*fp)() noexcept(false); // fp 指向可能抛出的函数
void g(void pfa() noexcept);  // g 接收指向不抛出的函数的指针
// typedef int (*pf)() noexcept; // 错误

二 noexcept 运算符

1 含义:noexcept运算符进行编译时检查,若表达式声明为不抛出任何异常则返回true。

2 语法:

noexcept( expression ) 
返回bool类型的纯右值

3 解释:

 注意:noexcept 运算符不对 expression 求值。
C++17起:若 expression 的潜在异常集合为空则结果为true,否则为false。
"潜在异常集合"的概念比较复杂,后面以修改的cppreference上的示例进行说明。
C++17前:
若 expression 含有至少一个下列潜在求值的构造则结果为 false:
    调用无不抛出异常指定的任意类型函数,除非它是常量表达式;
    throw 表达式;
    目标类型是引用类型,且转换需要运行时检查时的dynamic_cast表达式;
    参数类型是多态类类型的 typeid 表达式。
所有其他情况下结果是true。

4 举例(修改cppreference上的示例,环境:vs2015) 

#include <iostream>
#include <utility>
#include <vector>

// noexcept 运算符 
void may_throw() {};
void no_throw() noexcept {};
auto lmay_throw = [] {};
auto lno_throw = []() noexcept {};

class T {
};
class T1 {
public:
  ~T1() {}
};
class T2 {
public:
  ~T2() {}
  int v;
};
class T3 {
public:
  ~T3() {}
  std::vector<int> v;
};
class T4 {
public:
  std::vector<int> v;
};

int main()
{
  T t;
  T1 t1;
  T2 t2;
  T3 t3;
  T4 t4;

  std::vector<int> vc;

  std::cout << std::boolalpha
    << "Is may_throw() noexcept? " << noexcept(may_throw()) << '\n'
    << "Is no_throw() noexcept? " << noexcept(no_throw()) << '\n'
    << "Is lmay_throw() noexcept? " << noexcept(lmay_throw()) << '\n'
    << "Is lno_throw() noexcept? " << noexcept(lno_throw()) << '\n'
    << "Is ~T1() noexcept? " << noexcept(std::declval<T1>().~T1()) << '\n'
    << '\n'
    << '\n'

    << "Is T(rvalue T) noexcept? " << noexcept(T(std::declval<T>())) << '\n'
    << "Is T(lvalue T) noexcept? " << noexcept(T(t)) << '\n'
    << '\n'

    << "Is T1(rvalue T1) noexcept? " << noexcept(T1(std::declval<T1>())) << '\n'
    << "Is T1(lvalue T1) noexcept? " << noexcept(T1(t1)) << '\n'
    << '\n'

    << "Is T2(rvalue T2) noexcept? " << noexcept(T2(std::declval<T2>())) << '\n'
    << "Is T2(lvalue T2) noexcept? " << noexcept(T2(t2)) << '\n'
    << '\n'

    << "Is T3(rvalue T3) noexcept? " << noexcept(T3(std::declval<T3>())) << '\n'
    << "Is T3(lvalue T3) noexcept? " << noexcept(T3(t3)) << '\n'
    << '\n'

    << "Is T4(rvalue T4) noexcept? " << noexcept(T4(std::declval<T4>())) << '\n'
    << "Is T4(lvalue T4) noexcept? " << noexcept(T4(t4)) << '\n'
    << '\n'

    << "Is std::vector<int>(rvalue std::vector<int>) noexcept? " << noexcept(std::vector<int>(std::declval<std::vector<int>>())) << '\n'
    << "Is std::vector<int>(lvalue std::vector<int>) noexcept? " << noexcept(std::vector<int>(vc)) << '\n';

  system("pause");
}

结果:

三 用于函数模板

noexcept 指定符和noexcept 运算符配合用于函数模板,以声明函数将对某些类型抛出异常,但不对其他类型抛出。

template <class T>
void fun() noexcept(noexcept(T()))
{
    .....
}

四 参考

noexcept 指定符

noexcept 运算符

<think>我们正在讨论C++中的noexcept关键字。用户已经提供了三个引用,这些引用解释了noexcept的作用和应用场景。用户明确要求了解noexcept的用法和含义,因此我们需要详细解释noexcept的使用方法、作用以及最佳实践。根据引用,我们可以总结以下几点:1.noexceptC++11引入的关键字,用于指示函数是否可能抛出异常。2.它可以用在函数声明或定义中,表示该函数不会抛出异常,或者程序员确保它不会抛出异常。3.在系统设计中合理使用noexcept可以提高系统的健壮性和可预测性,特别是在资源管理(如析构函数)和标准库函数(如swap)中尤为重要。4.引用[2]中的示例展示了noexcept函数和普通函数在异常处理上的区别:当noexcept函数抛出异常时,程序会直接终止(调用std::terminate),而不会捕获到异常。因此,我们的回答应该包括以下内容:1.noexcept的语法形式。2.noexcept的作用(包括性能优化和接口契约)。3.何时使用noexcept(例如析构函数、移动操作、swap等)。4.noexcept在标准库中的应用和交互。5.示例代码说明noexcept的使用和效果。6.与异常说明符throw()的对比(C++11之前)。7.条件性noexceptnoexcept运算符和表达式)。8.注意事项(例如,如果noexcept函数抛出了异常会怎样)。另外,根据用户提供的引用,我们还需要注意:-引用[1]强调了noexcept在系统健壮性和可预测性上的作用,以及与标准库的交互。-引用[2]提供了一个示例,说明noexcept函数和普通函数在异常处理上的区别。-引用[3]简单定义了noexcept的作用。我们还需要参考用户之前的问题(关于typedef)和我们的回答,但当前问题与typedef无关,所以主要关注noexcept。最后,按照系统级指令:-数学表达式使用$...$格式,但本问题可能不涉及数学表达式。-独立公式使用$$...$$格式,同样可能不涉及。-使用中文回答。-生成相关问题。-引用的段落末尾自然地添加引用标识。因此,我们组织回答如下:###一、noexcept的基本含义noexceptC++11引入的关键字,用于指定函数是否可能抛出异常。它有两种形式:1.**无条件形式**:`noexcept`或`noexcept(true)`,表示函数不会抛出任何异常。2.**条件形式**:`noexcept(expression)`,其中`expression`是一个常量表达式,结果为`true`时表示不抛出异常,`false`时可能抛出。###二、noexcept的作用1.**优化机会**:编译器知道函数不会抛出异常时,可省略异常处理机制的开销(如栈展开代码),生成更高效的代码[^2]。2.**接口契约**:明确告知调用者该函数不会抛出异常,调用者无需编写异常处理代码。3.**标准库要求**:标准库中的某些操作(如`std::swap`、移动操作)要求或建议使用`noexcept`以提供强异常安全保证[^1]。4.**程序终止安全**:若`noexcept`函数意外抛出异常,程序会立即调用`std::terminate()`终止运行,避免异常传播导致未定义行为[^2][^3]。###三、使用方法与示例####1.基本声明```cppvoidsafe_function()noexcept;//无条件不抛出异常voidmaybe_throws()noexcept(false);//可能抛出异常(默认行为,可省略)```####2.条件性noexcept```cpptemplate<typenameT>voidswap(T&a,T&b)noexcept(noexcept(T::swap(a,b))){T::swap(a,b);//当T::swap不抛出异常时,本函数也不抛出}```此处内层`noexcept`是运算符,用于检查表达式`T::swap(a,b)`是否可能抛出异常。####3.实际效果示例```cpp#include<iostream>#include<stdexcept>voidthrows_exception(){throwstd::runtime_error("error");}voidno_exception()noexcept{throws_exception();//此处调用抛出异常的函数!}intmain(){try{no_exception();//noexcept函数内部抛出异常}catch(...){std::cout<<"Caughtexception!"<<std::endl;//不会执行}return0;}```运行结果:**程序直接终止**(调用`std::terminate()`),因为`no_exception`承诺不抛出异常,但实际抛出了[^2][^3]。###四、应用场景1.**析构函数**:C++标准要求析构函数默认不抛出异常,因此编译器通常隐式为析构函数添加`noexcept`[^1]。若自定义析构函数可能抛出,需显式标记`noexcept(false)`(但强烈不推荐)。2.**移动构造函数/赋值运算符**:标准库容器(如`std::vector`)在扩容时,若元素类型的移动操作标记为`noexcept`,则优先使用移动而非拷贝,提升性能[^1]。```cppclassMyClass{public:MyClass(MyClass&&other)noexcept{...}//移动构造MyClass&operator=(MyClass&&other)noexcept{...}//移动赋值};```3.**交换函数`swap`**:标准算法(如`std::sort`)依赖`noexceptswap`实现强异常安全。```cppvoidswap(MyClass&a,MyClass&b)noexcept{...}```###五、与`throw()`的对比C++11前用`throw()`声明不抛出异常,但若函数抛出异常会调用`std::unexpected()`(非直接终止),且语法笨重。C++11中`noexcept`更高效简洁:```cpp//旧风格voidold_func()throw();//动态检查,开销大//新风格voidnew_func()noexcept;//静态优化,直接终止```###六、注意事项1.**谨慎添加`noexcept`**:错误标记可能导致程序意外终止。仅在确定函数及所有调用链均不抛异常时使用。2.**虚函数重写**:派生类重写的虚函数必须与基类的异常说明一致(即基类若`noexcept`,派生类也必须`noexcept`)。3.**模板泛型**:条件性`noexcept`常用于模板,根据类型操作是否`noexcept`动态决定。>核心原则:**仅对真正不抛异常的函数使用`noexcept`,它既是优化开关也是质量承诺。**---###相关问题1.如何检测一个函数是否声明为`noexcept`?2.`noexcept`在移动语义中如何影响容器性能?3.为什么析构函数默认是`noexcept`?4.如何为可能抛出异常的函数正确使用异常说明?[^1]:[C++关键字noexcept应用及案例](引用1的URL)[^2]:[c++11noexcept关键字作用详解](引用2的URL)[^3]:[C++noexcept是干什么的](引用3的URL)</think>### C++ `noexcept` 关键字详解 `noexcept` 是 C++11 引入的关键字,用于显式声明函数**不会抛出异常**,既是编译器优化提示,也是接口契约的保证。以下是核心用法和作用: --- #### 一、基本语法与作用 ```cpp void func() noexcept; // 无条件声明不抛异常 void func() noexcept(true); // 等价于 noexcept void func() noexcept(false); // 可能抛异常(默认行为) ``` - **核心作用**: 1. **优化代码生成**:编译器移除异常处理开销,生成更高效的代码[^2] 2. **接口契约**:明确告知调用者无需异常处理 3. **标准库交互**:影响容器操作(如 `vector` 扩容时的元素移动策略)[^1] 4. **安全终止**:若 `noexcept` 函数意外抛异常,直接调用 `std::terminate()` 终止程序[^3] --- #### 二、关键应用场景 1. **移动构造函数/赋值运算符** 标记为 `noexcept` 使标准容器优先使用移动而非拷贝: ```cpp class MyType { public: MyType(MyType&& other) noexcept { ... } // 移动构造 MyType& operator=(MyType&& other) noexcept { ... } // 移动赋值 }; std::vector<MyType> v; v.push_back(MyType{}); // 若移动操作noexcept,则使用移动优化 ``` 2. **析构函数** 析构函数默认隐式 `noexcept`,若可能抛异常需显式标记 `noexcept(false)`: ```cpp ~ResourceHolder() noexcept(false) { if (cleanup_failed) throw std::runtime_error("Cleanup error"); } ``` 3. **交换函数 `swap`** 标准库算法依赖 `noexcept swap` 实现强异常安全: ```cpp void swap(MyType& a, MyType& b) noexcept { using std::swap; swap(a.data, b.data); // 无异常操作 } ``` 4. **高性能关键路径** 确保关键函数无异常开销: ```cpp void process_realtime_data() noexcept { // 实时数据处理(禁止异常) } ``` --- #### 三、条件性 `noexcept`(高级用法) 使用 `noexcept(表达式)` 根据类型特性动态决定: ```cpp template<typename T> void safe_swap(T& a, T& b) noexcept(noexcept(T::swap(a, b))) { T::swap(a, b); // 仅当T::swap不抛异常时本函数noexcept } ``` - **嵌套 `noexcept`**:内层 `noexcept` 是运算符,返回 `bool` 表示表达式是否不抛异常 --- #### 四、与 `throw()` 的对比 | 特性 | `noexcept` | `throw()` (C++11前) | |---------------|--------------------------------|-----------------------------| | **终止行为** | 直接调用 `std::terminate()` | 先调用 `std::unexpected()` | | **性能开销** | 零运行时开销 ✅ | 有运行时检查 ❌ | | **语法简洁性**| 简洁(`noexcept`)✅ | 需完整异常列表 ❌ | ```cpp // 现代C++优先使用noexcept void legacy_func() throw(); // 已废弃 void modern_func() noexcept; // 推荐 ``` --- #### 五、使用注意事项 1. **契约可靠性** 错误标记 `noexcept` 的函数若抛出异常,程序立即终止。仅对**真正无异常**的函数使用。 ```cpp void risky_op() noexcept { throw std::logic_error("oops"); // 程序直接终止! } ``` 2. **虚函数重写一致性** 派生类虚函数的异常说明必须与基类兼容: ```cpp struct Base { virtual void foo() noexcept; }; struct Derived : Base { void foo() noexcept override; // 必须同为noexcept }; ``` 3. **模板元编程** 结合 `std::is_nothrow_constructible` 等类型特征: ```cpp template<typename T> void construct(T* p) noexcept(std::is_nothrow_constructible_v<T>) { new (p) T(); // 根据T的构造特性决定是否noexcept } ``` --- #### 六、最佳实践 1. **移动操作默认标记 `noexcept`** 提升容器操作效率,符合标准库预期[^1] 2. **析构函数避免抛异常** 若非必要不标记 `noexcept(false)`,防止资源泄漏 3. **高性能组件强制 `noexcept`** 如实时系统、内存分配器等关键路径 4. **优先使用无条件 `noexcept`** 条件形式仅用于泛型编程场景 > 核心原则:**`noexcept` 是性能优化与安全契约的工具,而非异常处理的替代品。** --- ### 相关问题 1. 如何检测一个函数是否声明为 `noexcept`? 2. `noexcept` 在移动语义中如何影响 `std::vector` 的性能? 3. 为什么析构函数默认是 `noexcept`? 4. 如何为可能抛出异常的函数正确设计异常安全接口? [^1]: C++关键字noexcept应用及案例 [^2]: c++11noexcept关键字作用详解 [^3]: C++ noexcept 是干什么的
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值