31

我看到C++ 11添加了noexcept关键字。但我真的不明白为什么它有用。

如果函数在不应该抛出的时候抛出 - 我为什么要让程序崩溃?

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

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

那时会发生什么?

4

2 回答 2

39

的主要用途noexcept是用于通用算法,例如,在调整 a的大小时std::vector<T>:对于移动元素的有效算法,有必要提前知道没有任何移动会抛出。如果移动元素可能会抛出,则需要复制元素。使用noexcept(expr)运算符,库实现可以确定是否可能抛出特定操作。不抛出操作的属性成为合约的一部分:如果违反该合约,所有赌注都将被取消,并且可能无法恢复有效状态。在造成更多损失之前进行救助是自然的选择。

为了传播关于noexcept操作的知识不要抛出,也有必要声明这样的函数。为此,您可以使用noexcept, throw(), ornoexcept(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它们可以在将来声明(并可能根据需要进行更改)。

于 2014-12-07T19:28:54.793 回答
4

程序可能崩溃的原因是因为noexcept告诉优化器您的代码不会抛出。如果确实如此 - 好吧,没有办法预测优化代码会发生什么。

至于 MSVC++,你必须检查它们实现时会发生什么noexcept。从标准的角度来看,SEH 是未定义的行为。访问受保护的内存现在可能已经崩溃。

于 2014-12-07T19:21:24.977 回答