3

我目前正试图了解 noexcept (就像几乎所有我避免使用旧的“运行时异常规范”的人一样)。虽然我认为我了解了 noexcept 的基本概念,但我不确定在以下情况下会发生什么:

class sample {
public:
  sample() noexcept { }//this doesn't throw
  sample(const sample & s) noexcept { }
  sample(sample && s) noexcept { }
  sample & operator=(const sample & s) noexcept {...}
  sample & operator=(sample && s) noexcept { ... }
  ~sample() noexcept() { }//this should never ever throw
  sample operator-() const { return *this * -1; }//assuming that there is a operator*…
  sample & operator*=(const sample & s) noexcept { ... }
};

sample operator*(sample s1, const sample & s2) { return s1 *= s2; }//same problem as with operator-…

将 sample::operator- 声明为 noexcept 是否安全?(考虑到它在返回时调用构造函数)

编辑:我更新了代码部分,因为问题的中心部分似乎不清楚......</p>

4

2 回答 2

2

编辑后:保证您的实现operator-不会抛出任何异常(好吧,至少如果您标记operator*noexcept,也就是说),因此将其标记为noexcept. 我真的不明白你的担忧,所以我可能会错过这个问题的原因。所有操作,包括潜在的复制或移动构造都被明确标记noexcept……问题出在哪里?


除非您明确标记它,noexcept否则它将不具有该资格。现在,取决于实现operator*复制构造函数,它实际上可能永远不会抛出,但这并没有使它成为noexcept.

copy-constructor开始,如果您不定义它,则隐式声明的复制构造函数将noexcept取决于您的类型的所有成员是否是noexcept(同样,不仅它们不抛出,而且它们具有那个资格)

于 2012-03-17T20:33:38.133 回答
1

你的问题是,“它安全吗?”。我宁愿问,“仅仅因为它们不抛出异常而将某些函数声明为 noexcept 是否有意义?”。

严格来说,noexcept 提供了两件事:

  1. 如果您的函数声明为 noexcept,任何人都可以在编译时检查。(这实际上仅在一个标准函数中有用:move_if_noexcept)
  2. 您将获得运行时保证,即如果您的函数仍然试图抛出异常,它不会退出函数(因为改为调用 std::terminate)。

更有趣的是,列出 noexcept 不提供的功能:

  1. 如果函数真的没有抛出,在编译时自动检查
  2. 更重要的是,它不提供任何“异常安全保证”。

相信在学习 noexcept 的时候,第二项是经常被忽略的。如果你想提供一个不失败的保证(注意这与不抛出的保证不同,因为函数可能失败,但不会抛出),你可以简单地在你的函数中实现它而不抛出任何东西,并在某个地方记录它。您不需要将您的函数标记为 noexcept。无论如何,它将提供无故障异常安全性。

使用 noexcept 唯一合理的地方是在你的类型中提供移动构造函数或移动赋值时,并且只有当你觉得你需要自己定义它们而不是依赖于编译器生成的版本时。这很有用,因为一些 STL 容器操作会运行得更快,因为它们在实现中使用函数 std::move_if_noexcept。

我还推荐这篇关于 noexcept 功能细节的文章。

于 2012-07-05T09:00:19.317 回答