为何带转换构造函数的C++代码需要const拷贝构造函数?
A a = new int;在非const拷贝构造时编译失败? 先还原你的代码场景:
初始报错代码
// A.h class A { public: A(int *object) { std::cout << "convert"; } A(A &object) { std::cout << "copy"; } }; // main.cpp A a = new int;
编译时触发错误:
invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’
修改后正常运行的两种情况
- 给拷贝构造函数添加
const修饰:
A(const A &object) { std::cout << "copy"; }
编译通过,且运行只输出convert。
2. 移除自定义拷贝构造函数,依赖编译器生成的默认版本,同样正常运行。
核心原因拆解
我们一步步拆解A a = new int;的执行逻辑:
- 创建临时右值对象
new int返回int*类型,会匹配你的A(int*)构造函数,生成一个临时的A对象——这是个典型的右值,它没有名字,生命周期极短,用完就会被销毁。 - 初始化对象
a的语法要求
按照C++的初始化语法,A a = 临时对象原本需要调用拷贝构造函数,把临时对象的内容拷贝给a。但这里有个关键规则:
C++明确规定,非const的左值引用(
A&)不能绑定到右值
你的初始拷贝构造函数是A(A&),它要求传入一个可修改的左值,但临时对象是右值,无法绑定到A&,所以编译器直接抛出错误。
为什么加const或移除拷贝构造就正常?
加
const的情况const A&是const左值引用,C++允许它绑定到右值(临时对象),这时候拷贝构造函数的调用条件就满足了。而你看到只输出convert,是因为编译器做了**拷贝省略(Copy Elision)**优化:它会跳过临时对象的创建,直接用new int作为参数构造a,相当于把代码优化成了A a(new int);,自然不会调用拷贝构造。移除自定义拷贝构造的情况
如果你不写自定义拷贝构造,编译器会自动生成一个默认的拷贝构造函数,它的签名正是A(const A&)——和你手动加const的版本完全一致,所以同样能绑定临时对象,同时也会触发拷贝省略优化,最终只调用转换构造函数。
补充:为什么你会以为不需要拷贝构造?
你觉得不需要拷贝构造,是因为最终运行时确实没调用它,但语法上这个初始化过程原本需要拷贝构造的支持,只是编译器帮你做了优化跳过了。但编译器做优化的前提是:拷贝构造函数必须是可访问且符合绑定规则的(比如用const A&)。如果你的拷贝构造不符合规则(比如A&),编译器连优化的机会都没有,直接就报错了。
内容的提问来源于stack exchange,提问作者myschu

