noexcept 有什么用途?

32

我看到C++ 11增加了noexcept关键字,但我不太明白它有什么用。

如果函数在不应该抛出异常时抛出异常-为什么我想让程序崩溃?

那我什么时候该使用它呢?

此外,如果使用/Eha进行编译和使用_set_se_translator,它将如何运作?这意味着任何一行代码都可能抛出c++异常-因为它可能会抛出SEH异常(由于访问受保护的内存),并将被转换为c++异常。

那时会发生什么?


1
你想使用 noexcept 来告诉系统一个函数不会抛出异常。如果它仍然抛出异常(因为某些开发人员很蠢,要么直接抛出异常,要么间接调用某些没有 try-catch 块的东西),那么你认为系统应该怎么做?显然,如果访问了不允许访问的内存,你的代码将以某种方式崩溃,那么如果它以这种或那种方式崩溃有什么区别呢? - Mats Petersson
我推荐这些文章: https://akrzemi1.wordpress.com/2011/06/10/using-noexcept/ https://akrzemi1.wordpress.com/2014/04/24/noexcept-what-for/ - Andrzej
程序会崩溃吗?我认为会调用std::terminate,这更像是一种“有序中止”而不是崩溃(这基本上意味着需要生成额外的调用terminate的代码)。 - Damon
https://blog.quasardb.net/when-noexcept-2/ - user972014
2个回答

41
noexcept的主要用途是用于泛型算法,例如调整std::vector<T>大小时:为了实现移动元素的高效算法,需要提前知道没有任何移动会抛出异常。如果移动元素可能会抛出异常,则需要复制元素。使用noexcept(expr)运算符,库实现可以确定特定操作是否可能抛出异常。不抛出异常的操作属性成为合同的一部分:如果违反了该合同,则所有赌注都将被取消,并且可能没有办法恢复有效状态。在造成更多损害之前放弃是自然的选择。
为了传播关于noexcept操作不抛出异常的知识,还需要声明函数为此类函数。为此,您可以使用noexceptthrow()noexcept(expr)与常量表达式。在实现通用数据结构时,使用表达式是必要的:通过表达式,可以确定任何类型相关操作是否可能引发异常。
例如,std::swap()的声明类似于这样:
template <typename T>
void swap(T& o1, T& o2) noexcept(noexcept(T(std::move(o1)) &&
                        noexcept(o1 = std::move(o2)));

基于 noexcept(swap(a, b)),库可以选择不同的高效实现某些操作:如果它可以在不引发异常的情况下只是简单地 swap(),则可能会暂时违反不变量并稍后恢复它们。如果可能会抛出异常,则库可能需要复制对象而不是在它们之间移动。
标准 C++ 库实现不依赖于许多操作为 noexcept(true) 是不太可能的。它将检查的操作可能主要涉及移动对象,即:
  1. 类的析构函数(请注意,析构函数默认情况下即使没有任何声明也是 noexcept(true);如果您有可能引发异常的析构函数,您需要将其声明为这样,例如:T::~T() noexcept(false))。
  2. 移动运算符,即移动构造函数(T::T(T&&))和移动赋值运算符(T::operator=(T&&))。
  3. 类型的 swap() 操作(swap(T&, T&) 和可能的成员版本 T::swap(T&))。
如果其中任何一个操作与默认值不同,您应该相应地声明它以获得最有效的实现。这些操作的生成版本声明它们是否引发异常,具体取决于用于成员和基类的相应操作。
虽然我可以想象将来或者某些特定库可能会添加一些操作,但是现在我可能不会将操作声明为 noexcept。如果出现其他函数,它们作为 noexcept 有所不同,则可以在将来声明它们(并根据需要进行更改)。

4
程序崩溃的原因是因为noexcept告诉优化器你的代码不会抛出异常。如果它确实抛出了异常,那么就没有办法预测优化后的代码会发生什么。
至于MSVC++,您需要检查他们实现noexcept时会发生什么。从标准的角度来看,SEH是未定义行为。访问受保护的内存已经可以导致程序崩溃。

1
出于好奇:什么使SEH成为未定义行为? - MFH
2
@MFH:简单来说,SEH在标准中没有出现,因此所有定义的行为都不是SEH。通过否定,所有SEH都不是定义行为。 - MSalters
1
@MFH:标准制定了规则。这些规则允许struct foo { int i_34568790845609; },即使该特定结构不出现在其中。然而,这些规则绝对不允许SEH。一组结构是无限的,并不意味着它包含所有东西。偶数集合是无限的,但不包含3。 - MSalters
1
@MSaltes:我没有不同意的地方,但是标准中的哪个规则决定了SEH的“实现特定性”——请注意,SEH不一定是由UB引起的,而也可以由软件发出(例如在参数无效的情况下)。在这方面,SEH似乎类似于posix信号,那么它也将是UB... - MFH
@MFH:直到几年前,未定义行为的意思不过是C标准本身并没有强加任何关于实现会做什么的要求。编译器和平台通常在某些情况下承诺会发生什么,为了让特定平台的代码依赖于由此所作出的承诺是完全合理的。然而,最近出现了一种哲学,认为编译器有权假定程序永远不会进入任何使未定义行为成为必然的状态。 - supercat
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接