在更有效的C ++ 中,Scott Meyers说
C ++指定复制作为异常抛出的对象。
我想,如果复制构造函数依次抛出异常,则会调用std::terminate
,因此这是声明所有异常'复制构造函数noexcept
的一个很好的理由(还有,我猜测,不要抛出从堆中分配内存的对象,如std::string
)。
然而,我惊讶地发现GCC 4.7.1附带的标准库实现没有为std::bad_alloc
和std::exception
定义那些复制构造函数。他们不应该定义它们noexcept
吗?
答案 0 :(得分:4)
第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, "");
即。如果某个实现没有使这些成员不受欢迎,那么它就不符合这一点。
同样,18.6.2.1 [bad.alloc] / p1也指定noexcept副本:
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;
};
}
此外,std定义的异常类型的全部具有显式或隐式的noexcept副本成员。对于<stdexcept>
中定义的类型,通常使用what()
字符串的引用计数缓冲区来实现。这在[例外] / p2中已经明确:
从类异常派生的每个标准库类
T
应有一个可公开访问的复制构造函数和一个公开的 无法退出的可访问复制赋值运算符 例外。 ...
也就是说,在质量实现中(并且在这方面不需要英雄才能创建高质量的实现),异常类型的复制成员不仅不会抛出异常(当然因为它们被标记为{{1} }),他们也不会拨打noexcept
。
没有用于复制std定义的异常类型的失败模式。要么没有要复制的数据,要么数据是引用计数和不可变的。
答案 1 :(得分:3)
异常的内存分配是在常规渠道之外完成的:
15.1抛出异常[except.throw]
3 抛出异常复制 - 初始化(8.5,12.8)临时 对象,称为异常对象。 [...]
4 异常对象的内存是 除非在3.7.4.1中指出,否则以未指明的方式分配。 [...]
3.7.4.1分配函数[basic.stc.dynamic.allocation]
4全局分配函数仅作为新结果调用 表达式(5.3.4),或直接使用函数调用语法调用 (5.2.2),或通过调用函数间接调用 C ++标准库。 [注意:特别是全球分配 不调用函数来为异常对象(15.1)分配 [...] 的存储空间。 -end note]
大多数实现都有一个单独的内存区域,从中分配异常对象,这样即使您重新抛出std::bad_alloc
异常对象,也不会要求耗尽的空闲存储本身分配复制的异常对象。因此,复制本身不应该有原因产生另一个异常。
答案 2 :(得分:1)
嗯,声明它noexcept
一切都很好,但它要求你可以保证它不会抛出异常(对于可移植代码,在它的所有实现中都是如此!)。我认为这是标准声明不是这样宣布的原因。
声明复制构造函数noexcept
显然没有坏处,但尝试实现这一点可能非常有限。