繁体   English   中英

可移动但不可复制的例外

[英]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_exceptionstd::current_exception 两个编译器都接受这个版本。 这应该清楚地表明,如果编译器在抛出异常时不需要复制构造函数,则在使用这两个函数时编译器将不需要一个复制构造函数。

current_exception表示它既指向当前异常,也指向它的副本,但不说明哪个。 这告诉我:

  • 它是否被复制[*]未指明
  • 因此,你的异常类不好(当然没有人可能会调用current_exception
  • 因此,它在某些实现中起作用也就不足为奇了。 有可能不会计提当前异常,除非有前两者之一的需求来自实施者或希望实现者避免拷贝不会被复制。

只是扔东西并通过引用捕获它是好的。 throw表达式中的临时“用于初始化”实现使用的对象以保持当前异常,因此可以移动或复制它(根据类支持),并且可以省略该移动/复制。

为了它的价值,指定make_exception_ptr 始终复制。 您可能会认为这是一个缺陷: e可以是一个按值参数,在这种情况下,移动可能会更好。 但这是我浮躁而无知的印象,我以前从未见过这些功能。

[*]显然未明确指出current_exception是否“每次调用时都会创建一个新副本”,但我并不完全确定这是否意味着它是否未指定是否在第一次调用时创建新副本。

不过,我有点意外。 据我所知,有几种情况可能会复制异常对象,例如std::current_exception()std::rethrow_exception()

但你不要打电话给他们。 该标准非常清楚异常对象的初始化方式。 从15.1开始,p3:

throw-expression初始化一个临时对象,称为异常对象,其类型是通过从throw的操作数的静态类型中删除任何顶级cv限定符并从“array of T”调整类型来确定的。 “函数返回T”分别为“指向T的指针”或“指向函数返回T的指针”。 临时是一个左值,用于初始化匹配处理程序中命名的变量(15.3)。 如果异常对象的类型是不完整类型或指向不完整类型的指针(可能是cv-qualified),则程序格式不正确。 除了这些限制和15.3中提到的类型匹配限制之外,throw的操作数被完全视为调用(5.2.2)中的函数参数或return语句的操作数。

简而言之,它的作用类似于按值返回类型:返回值/异常对象由您提供的表达式初始化。 因为您使用的表达式是临时的,所以它就像从函数返回临时表并调用移动构造函数一样。 当然,赔率很高,但这就是15.1,p5的结果:

当抛出的对象是类对象时,即使复制/移动操作被省略,也应该可以访问复制/移动构造函数和析构函数(12.8)。

返回值也是如此:在适当的情况下,通过复制/移动初始化初始化返回值。 因此,即使它们被省略,也必须能够获得适当的构造函数。

您不能以需要复制构造异常对象的方式抛出异常类。 所以你不能抛出一个左值; 你只能抛出一个prvalue或一个xvalue。

在标准中没有任何地方说它允许系统无缘无故地任意复制异常。 std::current_exception调用可能会复制它。 std::rethrow_exception调用可能会复制它。

但是如果你不调用那些明确复制你的异常对象的东西, 那么不允许 C ++这么做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM