noexcept是C++中用于声明函数不抛异常的编译期机制,分为操作符和规范符两种用法;作为规范符时承诺函数绝不抛异常,否则程序终止,相比运行时检查的throw()更高效安全;常用于析构函数、移动操作和swap等需强异常安全的场景;在模板中可实现条件noexcept,在继承中派生类虚函数不得弱化基类的noexcept承诺。

noexcept
throw()
noexcept
谈到
noexcept
首先是作为操作符。你可以把它想象成一个编译期能计算的布尔表达式,它告诉你某个表达式(通常是函数调用)是否被声明为
noexcept
noexcept(foo())
foo()
noexcept
true
false
noexcept
noexcept
然后,更常见也更核心的是作为异常规范符。当你写
void func() noexcept;
int calculate() noexcept { /* ... */ }func
calculate
noexcept
catch
std::terminate()
#include <iostream>
#include <vector>
#include <string>
// 1. 作为异常规范符
void safe_function() noexcept {
// 这是一个承诺,此函数不会抛出异常
std::cout << "This is a noexcept function." << std::endl;
// 如果这里调用了一个可能抛异常的函数且未捕获,程序会terminate
// 例如:throw std::runtime_error("Oops!"); // 会导致程序终止
}
void possibly_throwing_function() {
std::cout << "This function might throw." << std::endl;
// throw std::runtime_error("Something went wrong!"); // 这是一个可能抛异常的函数
}
// 2. 作为noexcept操作符
template<typename T>
void process_value(T val) noexcept(noexcept(T(val))) { // noexcept(noexcept(T(val)))
// 这里的noexcept属性取决于T的构造函数是否是noexcept的
std::cout << "Processing value. Is this function noexcept? "
<< std::boolalpha << noexcept(process_value(val)) << std::endl;
}
struct MyClass {
MyClass() = default;
MyClass(const MyClass&) = default; // 拷贝构造函数
MyClass(MyClass&&) noexcept {} // 移动构造函数通常是noexcept的
void do_something() {
std::cout << "MyClass::do_something called." << std::endl;
}
};
int main() {
try {
safe_function();
possibly_throwing_function(); // 正常调用,如果抛出会被下面的catch捕获
} catch (const std::exception& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
// 使用noexcept操作符
std::cout << "Is safe_function() noexcept? " << std::boolalpha << noexcept(safe_function()) << std::endl;
std::cout << "Is possibly_throwing_function() noexcept? " << std::boolalpha << noexcept(possibly_throwing_function()) << std::endl;
MyClass mc;
process_value(mc); // MyClass的拷贝构造不是noexcept,所以process_value(mc)也不是noexcept
MyClass mc2 = std::move(mc); // 移动构造是noexcept的
return 0;
}可以看到,
noexcept
noexcept
throw()
这个问题问得特别好,因为它触及了C++异常处理机制演进的关键点。老实说,我个人觉得C++早期那个
throw()
throw()
void func() throw();
throw()
std::unexpected()
std::unexpected()
std::terminate()
noexcept
首先,
throw()
std::unexpected()
std::unexpected()
throw()
而
noexcept
noexcept
std::terminate()
简单来说,
throw()
noexcept
noexcept
noexcept
这其实是个工程决策问题,不是说所有函数都无脑加
noexcept
noexcept
最典型的应用场景,也是你几乎应该无条件考虑使用
noexcept
std::terminate()
noexcept
其次是移动构造函数和移动赋值运算符。比如
std::vector
noexcept
std::vector
noexcept
再来就是交换函数(swap)。一个
swap
swap
还有一些简单的、不会失败的工具函数或查询函数,比如纯粹的计算函数、只读的getter方法等。这些函数没有理由抛出异常,将其标记为
noexcept
总的来说,当你能百分之百确定一个函数不会、也不应该抛出异常时,就勇敢地加上
noexcept
noexcept
noexcept
noexcept
这部分内容其实挺有意思的,因为它涉及到
noexcept
先说模板。
noexcept
noexcept
noexcept
noexcept
例如,一个通用的
swap
template<typename T>
void my_swap(T& a, T& b) noexcept(noexcept(a.swap(b))) {
// 优先调用成员swap,如果成员swap不存在,则使用std::swap
using std::swap;
swap(a, b);
}这里
noexcept(noexcept(a.swap(b)))
T
swap
noexcept
my_swap
noexcept
T
noexcept
swap
swap
std::swap
std::swap
noexcept
my_swap
noexcept
再聊聊多态,也就是虚函数的情况。这里有一个很重要的规则,可以概括为“派生类的虚函数不能比基类的对应虚函数抛出更多的异常”。对于
noexcept
如果基类的虚函数是
noexcept
noexcept
noexcept
class Base {
public:
virtual void foo() noexcept { /* ... */ }
};
class Derived : public Base {
public:
// virtual void foo() { /* ... */ } // 错误:不能移除noexcept
virtual void foo() noexcept override { /* ... */ } // 正确
};如果基类的虚函数不是
noexcept
noexcept
class Base2 {
public:
virtual void bar() { /* ... */ } // 可能抛异常
};
class Derived2 : public Base2 {
public:
virtual void bar() noexcept override { /* ... */ } // 正确:加强了保证
// 或者 virtual void bar() override { /* ... */ } // 也正确:保持和基类一样
};这个规则确保了通过基类指针或引用调用虚函数时,其异常行为不会比预期的更糟糕。当你看到一个基类接口声明了
noexcept
所以,无论是写模板还是设计类继承体系,
noexcept
以上就是noexcept运算符怎么用 异常规范条件判断的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号