我的复制构造函数旁边有一个noexcept说明符。
#include <memory>
#include <vector>
class Foo final {
public:
Foo() noexcept = default;
Foo(const Foo& oth) : impl_(std::make_unique<Foo::Impl>()) {} // <---
~Foo() noexcept = default;
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
class Foo::Impl {
...
private:
std::vector<int> some_data;
}
我不确定是否应该将noexcept
放在复制构造函数旁边,而有std::make_unique
可以抛出bad_alloc
。
任何帮助将不胜感激!
答案 0 :(得分:4)
E.12: Use noexcept when exiting a function because of a throw is impossible or unacceptable
中的cpp编码准则非常清楚因此,即使该函数/ ctor的调用可能导致异常,也可以使用noexcept
,如果您认为该异常会导致应用程序处于无法处理的状态,那么您就可以使用它。
指南中的示例:
vector<double> munge(const vector<double>& v) noexcept { vector<double> v2(v.size()); // ... do something ... }
此处的
noexcept
表示我不愿意或无法处理无法构造局部向量的情况。也就是说,我认为内存耗尽是一个严重的设计错误(与硬件故障相当),因此,如果发生这种情况,我愿意使其崩溃。
因此,如果可以使用Foo
块来处理try-catch
的失败构造,而不会出现严重问题。这样您就不会在其中使用noexcept
。
答案 1 :(得分:3)
思考其他一些答案:即使某个函数潜在地抛出,我也不会使用noexcept
,即使您不在乎程序最终是否会终止也是如此。因为这样会引发声明为noexcept
的函数。
声明函数noexcept
会为您的类用户保留语义信息,他们可以依赖此信息,在您的情况下,这实际上是不正确的。
编辑:我建议您阅读Scott Meyers的有效现代C ++的第14项,它很好地描述了使用noexcept的好处以及何时使用它。
答案 2 :(得分:1)
我认为这确实取决于具体情况。如果可以合理地处理该异常(并期望将其抛出),则不应将其标记为noexcept
。另一方面,如果您的程序无法从异常中恢复,则最好将其标记为noexcept
。
我要说的第二种情况是,要分配的对象很小(如果您不能分配单个char
,则将无法进行很多异常处理)。
第一个应该发生在非常大的对象上(例如,您可以推迟分配)。话虽如此,如果您要复制一个巨大的对象...为什么不移动它呢?
答案 3 :(得分:1)
简单答案:请勿将其声明为noexcept,如果您知道它可能会抛出并且您没有充分的理由这样做(例如,您希望您的应用程序调用std :::如果无法复制,则终止。)
问问自己会有什么收获。编译器是否可以优化任何东西?我没有看到很多情况会发生这种情况(最常见的情况是我看到优化是针对移动的,因为std-library容器可以使用它-他们检查移动操作是否正常,这样他们才能保持保证)。您可能要使用的另一个地方是当您要记录函数不能抛出的内容时。显然不是这种情况。
另一方面,您将不再能够从可能的异常中恢复,并且程序将终止。
因此,在夏季,您什么也得不到,但是您可能会有所损失。
另请参阅核心准则:
E.12:由于不可能或不可接受的抛出而在退出函数时使用noexcept
此规则的标题可能会有些混乱。它说您应该将一个函数声明为noexcept,如果
- 它不会抛出或
- 您不在乎是否有例外情况。您愿意使程序崩溃,因为由于内存耗尽而无法处理诸如std :: bad_alloc之类的异常。
如果您是对象的直接所有者,则抛出异常不是一个好主意。
顺便说一下,以下特殊成员函数隐式地为noexcept:
答案 4 :(得分:1)
何时使用noexcept
“最佳做法”
不必考虑在每个函数声明之后是否需要附加
noexcept
,这将大大降低程序员的工作效率(坦率地说,这很痛苦)。
然后在明显无法使用该函数的情况下使用它。
使用
noexcept
之后,我什么时候可以实际期望获得性能改善? [...]我个人很关心noexcept
,因为为编译器提供了增加的自由度,可以安全地应用某些优化。
似乎最大的优化收益来自用户优化,而不是编译器优化,原因是可以检查noexcept
并对其进行重载。大多数编译器都遵循无罚即弃的异常处理方法,因此我怀疑它会在代码的机器代码级别上发生很大的变化(或发生任何变化),尽管也许可以通过删除处理来减少二进制大小代码。
在大四列中使用noexcept
(构造函数,赋值,而不是已经使用noexcept
的析构函数)可能会带来最佳的改进,因为noexcept
检查在模板中是“常见”的代码,例如在std容器中。例如,std::vector
不会使用您的类的移动,除非它被标记为noexcept
(否则编译器可以推断出来)。