为何用纯右值(prvalue)直接初始化基类成员需拷贝/移动构造函数?
为什么删除拷贝构造函数的基类,在派生类初始化列表中用纯右值初始化会报错?
我们先看一个能正常编译的例子:对于删除了拷贝构造函数的类,确实可以通过纯右值(比如函数返回值)直接初始化对象,比如下面的代码:
struct B { B(int i, std::string s) {} B(const B&) = delete; }; B Factory() { return {1, "ss"}; } int main() { B obj(Factory()); return 0; }
但当添加如下派生类代码后:
struct D : public B { D() : B(Factory()) {} };
会触发编译错误:
main.cpp:22:40: error: use of deleted function ‘B::B(const B&)’
问题根源
这个问题和C++标准中复制消除的规则演变有关:
首先看C++标准的相关规定:
- 11.9.4(7)条款:
mem-initializer中的expression-list或braced-init-list将按照9.4条款的直接初始化规则,用于初始化指定子对象(或委托构造函数中的完整类对象)。
- 9.4(17.6.1)条款:
若初始化表达式为纯右值,且源类型的cv非限定版本与目标类相同,则该初始化表达式将用于初始化目标对象。
从条款描述来看,基类子对象的初始化应该适用纯右值直接初始化的规则,但实际报错的原因在于:
- 在C++17之前,复制消除(即跳过拷贝/移动构造,直接用纯右值初始化目标对象)是编译器的可选优化,而非强制要求。部分编译器在处理基类子对象的初始化时,没有执行这个优化,而是尝试调用被删除的拷贝构造函数,从而触发错误。
- 到了C17标准,复制消除被改为强制规则,编译器必须直接用纯右值初始化目标对象,不再依赖拷贝/移动构造函数,因此这段代码在C17及更高版本中可以正常编译。
内容的提问来源于stack exchange,提问作者Ari
相关产品推荐
相关产品推荐

