可移动但不可复制的异常
Moveable but Non-Copyable Exceptions
我正在考虑编写不可复制的异常类。我觉得这很有趣,因为这样我就不必担心在复制构造函数中分配时可能引发的异常。如果异常对象的创建成功,则一切正常,std::terminate
应该不会出现问题。
struct exception
{
exception() = default;
exception(const exception&) = delete;
exception(exception&&) noexcept = default;
~exception() noexcept = default;
auto operator=(const exception&) -> exception& = delete;
auto operator=(exception&&) noexcept -> exception& = delete;
};
int main()
{
try {
try {
throw exception{};
} catch (...) {
std::rethrow_exception(
std::current_exception());
}
} catch (const exception& e) {
return 1;
}
}
GCC-4.7和Clang-3.2接受上述代码。然而,我有点惊讶。据我所知,有几种情况下可能会复制异常对象,例如std::current_exception()
和std::rethrow_exception()
。
问题:根据C++11,上述代码是否正确,即是否会被所有符合C++11的编译器接受?
编辑:将std::rethrow_exception
和std::current_exception
添加到示例中。两个编译器都接受此版本。这应该清楚地表明,如果在抛出异常时编译器不需要复制构造函数,那么在使用这两个函数时编译器就不需要复制构造器。
current_exception
表示它引用当前异常或它的副本,但没有说明是哪一个。这向我表明:
- 未指定是否复制[*]
- 因此,您的异常类不好(如果有人可能会调用它的
current_exception
,则肯定不会) - 因此,它在某些实现中工作也就不足为奇了。除非实现者对此有要求,或者希望实现者避免复制,否则可能不会对当前异常进行复制
只要扔东西并通过引用接住它就可以了。throw
表达式中的临时"用于初始化"实现用于保持当前异常的对象,因此可以移动或复制(根据类支持的情况),并且可以消除该移动/复制。
就其价值而言,make_exception_ptr
被指定为始终复制。您可能会认为这是一个缺陷:e
可以是一个按值参数,在这种情况下移动可能会更好。但这是我冲动和无知的印象,我以前从未见过这些功能。
[*]current_exception
是否"每次调用时都会创建一个新副本",这一点没有明确说明,但我不完全确定这是否意味着它在第一次调用时是否会创建新副本。
然而,我有点惊讶。据我所知,有几种情况下可能会复制异常对象,例如
std::current_exception()
和std::rethrow_exception()
。
但你不会给他们中的任何一个打电话。该标准非常清楚如何初始化异常对象。从15.1开始,p3:
throw表达式初始化一个临时对象,称为异常对象,其类型是通过从throw操作数的静态类型中删除任何顶级cv限定符s并将类型分别从"T的数组"或"返回T的函数"调整为"指向T的指针"或"指向返回T的函式的指针"来确定的。temporary是一个左值,用于初始化匹配处理程序(15.3)中命名的变量。如果异常对象的类型是不完整类型或指向除void(可能是cv限定的)之外的不完整类型的指针,则程序格式错误。除了这些限制和15.3中提到的对类型匹配的限制外,throw的操作数在调用(5.2.2)或返回语句的操作数。
简而言之,它的作用类似于按值返回类型:返回值/异常对象由您提供的表达式初始化。因为您使用的表达式是一个临时表达式,所以它的作用就像从函数返回一个临时函数并调用move构造函数。诚然,这很有可能被忽略,但这就是15.1,p5的观点:
当抛出的对象是类对象时,即使复制/移动操作被取消,复制/移动构造函数和析构函数也应该是可访问的(12.8)
返回值也是如此:在适当的情况下,通过复制/移动初始化来初始化返回值。因此,适当的构造函数必须是可访问的,即使它们被省略了。
不能以需要异常对象的复制构造的方式抛出异常类。所以你不能抛出左值;您只能抛出prvalue或xvalue。
标准中没有任何地方规定允许系统无故任意复制异常。调用std::current_exception
可能会复制它。调用std::rethrow_exception
可能会复制。
但是,如果您不调用显式复制异常对象的东西,则不允许随意调用C++。
- 简单可复制与可简单复制
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 对于参加可复制和可移动类的访问者来说,应该有多少过载?
- 可变参数宏:无法通过"..."传递非平凡可复制类型的对象
- 为什么 std::atomic<std::string> 会给出微不足道的可复制错误?
- 我可以隐式地创建一个琐碎的可复制类型吗
- 是std::memcpy在不同的可复制类型之间的未定义行为
- 为什么一对常量是微不足道的可复制的,而对不是?
- 在一个微不足道的可复制结构中,移动语义应该实现吗?
- 防止作用域枚举可复制/可移动
- C :对象上的可复制视图
- 防御性地应用 std::move 到平凡可复制的类型是否不可取
- 为什么 std::function 本身是可复制构造的类型?
- C++不可复制的 lambda 的行为是可复制的
- 错误:无法通过'...'传递非平凡可复制类型的对象'class boost::filesystem::path'
- 不能让类是微不足道的可复制的。我做错了什么?
- 使用临时存储区复制普通的可复制类型:允许吗
- 使用realloc可以安全地重新分配琐碎的可复制对象的存储吗
- 为什么Boost.Asio处理程序必须是可复制的
- 我需要可复制的缓冲区,尽可能轻(例如,不初始化零)