我应该声明我的异常的副本构造函数noexcept吗? P>在更有效的C++中,Scott Meyers说

我应该声明我的异常的副本构造函数noexcept吗? P>在更有效的C++中,Scott Meyers说,c++,exception,c++11,copy-constructor,noexcept,C++,Exception,C++11,Copy Constructor,Noexcept,C++指定复制作为异常抛出的对象 我想,如果复制构造函数依次抛出一个异常,就会调用std::terminate,因此这是声明我所有异常的复制构造函数noexcept(而且,我想,不要抛出从堆中分配内存的对象,比如std::string) 然而,我惊讶地发现GCC4.7.1附带的标准库实现没有为std::bad_alloc和std::exception定义那些复制构造函数。他们不应该定义它们吗?好吧,声明它是好的,但是它要求您可以保证它不会抛出异常(对于可移植代码,在它的所有实现中!)。我想这就是

C++指定复制作为异常抛出的对象

我想,如果复制构造函数依次抛出一个异常,就会调用
std::terminate
,因此这是声明我所有异常的复制构造函数
noexcept
(而且,我想,不要抛出从堆中分配内存的对象,比如
std::string


然而,我惊讶地发现GCC4.7.1附带的标准库实现没有为
std::bad_alloc
std::exception
定义那些复制构造函数。他们不应该定义它们吗?

好吧,声明它是好的,但是它要求您可以保证它不会抛出异常(对于可移植代码,在它的所有实现中!)。我想这就是为什么标准版本没有这样声明的原因


声明复制构造函数显然没有什么害处,但要实现这一点可能会有很大的限制。

异常的内存分配是在常规通道之外完成的:

15.1引发异常[除外.throw]

3抛出异常副本会初始化(8.5,12.8)临时副本 对象,称为异常对象。[…]

4异常对象的内存为 以未指定的方式分配,3.7.4.1中注明的除外。[…]

3.7.4.1分配功能[基本.stc.dynamic.Allocation]

4全局分配函数仅在新分配的结果中调用 表达式(5.3.4),或使用函数调用语法直接调用 (5.2.2),或通过调用 C++标准库。[注:特别是,全球分配 未调用函数为异常对象(15.1)的[…]分配存储。-结束说明]


大多数实现都有一个单独的内存区域,从中分配异常对象,因此即使您重新抛出一个
std::bad_alloc
异常对象,也不会要求耗尽的空闲存储本身分配复制的异常对象。因此,复制本身不应产生另一个异常。第18.8.1节[异常]/p1规定:

namespace std {
    class exception {
    public:
      exception() noexcept;
      exception(const exception&) noexcept;
      exception& operator=(const exception&) noexcept;
      virtual ~exception();
      virtual const char* what() const noexcept;
  };
}
即,std::exception的副本构造函数和副本分配应为
noexcept
,这可通过以下方式进行测试:

static_assert(std::is_nothrow_copy_constructible<std::exception>::value, "");
static_assert(std::is_nothrow_copy_assignable<std::exception>::value, "");
此外,所有std定义的异常类型都没有显式或隐式的异常副本成员。对于
中定义的类型,这通常通过
what()
字符串的引用计数缓冲区来实现。这一点在[exception]/p2中有明确说明:

从类异常派生的每个标准库类
T
应具有可公开访问的副本构造函数和 可访问的复制分配运算符,该运算符不带 例外

也就是说,在质量实现中(在这方面创建质量实现并不需要英勇),异常类型的复制成员不仅不会抛出异常(因为它们被标记为
noexcept
),而且也不会调用
terminate()


复制std定义的异常类型没有故障模式。要么没有要复制的数据,要么数据是引用计数且不可变的。

标准不应该保证其异常“复制构造函数”不会抛出吗?这可能是不可能的。因此,我正确地理解了“不带异常退出”这一短语允许较差的实现调用std::terminate吗(即在体内抛出一个异常,并作为noexcept调用terminate的结果)?@Johanneschaub litb:我最近读到了另一个SO问题的答案,该问题建议
std::chrono::seconds
的构造函数可以调用
this_thread::sleep
“一个如此荒谬的实现,以至于它在市场上的时间可以用毫秒来衡量“是的,当然。该标准不是一份完美的文件,也永远无法100%地防范这种荒谬。我希望它甚至不会尝试。它需要做的事情远比这重要。但这并不能回答问题,不是吗?请说“我的例外”在标题中,这意味着用户定义的异常类。您只是在谈论std定义的异常类。也许您只需要添加一些告诫,以遵循标准的示例。
namespace std {
       class bad_alloc : public exception {
       public:
         bad_alloc() noexcept;
         bad_alloc(const bad_alloc&) noexcept;
         bad_alloc& operator=(const bad_alloc&) noexcept;
         virtual const char* what() const noexcept;
  };
}