可移动但不可复制的异常 [英] Moveable but Non-Copyable Exceptions

查看:107
本文介绍了可移动但不可复制的异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在考虑编写不可复制的异常类。这似乎是一个好主意,因为显然没有投掷复制构造函数的问题。如果异常对象的创建成功,一切都很好,没有问题 std :: terminate

I am thinking about writing non-copyable exception classes. This seems to be a good idea, because there is obviously no problem with throwing copy-constructors. If the creation of the exception object succeeds, everything is fine and no issues with 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()

GCC-4.7 and Clang-3.2 accept the above code. However, I am a bit surprised. As far as I know, there are several situations where exception objects might be copied, e.g. std::current_exception() and std::rethrow_exception().

问题:根据C ++ 11,上述代码是否正确,即所有符合C ++ 11的编译器是否接受?

Question: Is the above code correct according to C++11, i.e. will it be accepted by all compilers conforming to C++11?

已编辑:添加了 std :: rethrow_exception std :: current_exception 到示例。两个编译器都接受此版本。这应该清楚,如果编译器不需要一个复制构造函数时抛出一个异常,编译器将不需要一个当这两个函数使用时。

Edited: Added std::rethrow_exception and std::current_exception to the example. Both compilers accept this version. This should make it clear, that if the compiler doesn't require a copy-constructor when an exception is thrown, the compiler won't require one when these two functions are used.

推荐答案


但是,我有点惊讶。据我所知,有几种情况下异常对象可能被复制,例如。 std :: current_exception() std :: rethrow_exception()

但是你不能调用任何它们。该标准非常清楚如何初始化异常对象。从15.1起,p3:

But you don't call any of them. The standard is quite clear about how the exception object is initialized. From 15.1, p3:


throw-expression初始化一个临时对象,称为异常对象,从T的操作数的静态类型和从T的数组或函数返回T到指向T的指针或返回T的函数的指针的类型分别调用顶级cv限定符。 temporary是一个左值,用于初始化在匹配处理程序(15.3)中命名的变量。如果异常对象的类型是不完全类型或指向不是(可能是cv限定的)void的不完整类型的指针,则程序是不成形的。除了这些限制和15.3中提到的类型匹配的限制外,throw的操作数完全作为
调用(5.2.2)中的函数参数或return语句的操作数处理。

A throw-expression initializes a temporary object, called the exception object , the type of which is determined by removing any top-level cv-qualifier s from the static type of the operand of throw and adjusting the type from "array of T" or "function returning T" to "pointer to T" or "pointer to function returning T", respectively. The temporary is an lvalue and is used to initialize the variable named in the matching handler (15.3). If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed. Except for these restrictions and the restrictions on type matching mentioned in 15.3, the operand of throw is treated exactly as a function argument in a call (5.2.2) or the operand of a return statement.

简而言之,它的作用就像按值返回一个类型:return value / exception对象通过你提供的表达式初始化。因为你使用的表达式是一个临时的,它将像从函数返回一个临时函数并调用move构造函数。如果被抛出的对象是一个类,那么它将被舍弃,但是这是15.1,p5使得:

In short, it acts like returning a type by value: the return value/exception object is initialized by the expression you provide. Because the expression you use is a temporary, it will act like returning a temporary from a function and invoke the move constructor. Granted, odds are good that this will be elided, but that's the point that 15.1, p5 makes:


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

When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible, even if the copy/move operation is elided (12.8).

这对于返回值也是如此:在适当的情况下,通过复制/移动初始化来初始化返回值。因此,合适的构造函数必须是可访问的,即使它们被省略。

This is just as true for returning values: the return value is initialized by copy/move initialization, where appropriate. Thus, the appropriate constructors have to be accessible, even if they're elided.

你不能抛出你的异常类,异常对象。所以你不能抛出一个左值;你只能抛出一个prvalue或一个xvalue。

You can't throw your exception class in a way that would require copy-construction of the exception object. So you can't throw an lvalue; you can only throw a prvalue or an xvalue.

在标准中没有说系统允许任意复制异常没有理由。调用 std :: current_exception 可能会复制它。调用 std :: rethrow_exception 可能会复制它。

Nowhere in the standard does it say that the system is allowed to arbitrarily copy the exception for no reason. A call to std::current_exception may copy it. A call to std::rethrow_exception will likely copy it.

但是如果你不明确调用复制您的异常对象,C ++不允许这样做。

But if you don't call things that explicitly copy your exception objects, C++ is not allowed to do so willy-nilly.

这篇关于可移动但不可复制的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆