按值返回,不例外

return by value and noexcept

本文关键字:返回      更新时间:2023-10-16

我目前正在尝试摆脱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- 声明为 noexexcept 是否安全?(考虑到它在返回时调用构造函数(

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

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


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

对于复制构造

函数,如果您不定义它,隐式声明的复制构造函数将noexcept或不取决于是否noexcept类型的所有成员(同样,不仅他们不抛出,而且他们有这个资格(

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

严格来说,有两件事是没有提供的:

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

更有趣的是,列出noexcept 不提供的内容:

  1. 在编译时自动检查函数是否真的没有抛出
  2. 更重要的是,它不提供任何"例外安全保证"。

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

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

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