首页 > 解决方案 > 不可复制的类可以在 C++ 中被值捕获吗?

问题描述

在下一个程序中,B带有已删除复制构造函数的结构被抛出并按值捕获:

struct B {
    B() = default;
    B(const B&) = delete;
};

int main() {
    try {
        throw B{};
    }
    catch( B ) {
    }
}

Clang 拒绝代码并出现预期错误:

error: call to deleted constructor of 'B'
    catch( B ) {

但是 GCC 很好地接受了该程序,演示:https ://gcc.godbolt.org/z/ed45YKKo5

哪个编译器在这里?

标签: c++exceptioninitializationlanguage-lawyercopy-initialization

解决方案


叮当是正确的。(感谢@NathanOliver 的评论。)

[except.throw]/3

抛出异常复制初始化 ([dcl.init], [class.copy.ctor]) 一个临时对象,称为异常对象。表示临时值的左值用于初始化匹配处理程序中声明的变量([except.handle])。

[except.throw]/5

当抛出的对象是类对象时,为复制初始化选择的构造函数以及为复制初始化选择的构造函数将抛出的对象视为左值,即使复制/移动操作也应不可删除且可访问被省略([class.copy.elision])。

异常对象被认为是一个左值,在catch子句的参数的复制初始化中,选择了复制构造函数。复制操作可能会被忽略为优化;但是复制构造函数仍然必须存在并且可以访问。

我已将此报告为gcc bug 103048

顺便说一句,由于强制复制省略(自 C++17 起) ,导致的异常对象的复制初始化throw B{};很好。

在对象的初始化中,当初始化表达式是与变量类型相同的类类型(忽略 cv-qualification)的纯右值时:

T x = T(T(f())); // only one call to default constructor of T, to initialize x

首先,如果 T 是一个类类型并且初始化器是一个纯右值表达式,其 cv 非限定类型与 T 相同,则初始化器表达式本身,而不是从它的临时物化,用于初始化目标对象:见复制省略(C++17 起)


推荐阅读