为何C++移动构造函数未被调用?
问题
定义了一个同时包含拷贝构造函数和移动构造函数的类,拷贝构造函数可正常运行,但调用移动构造函数时无法生效。代码如下:
#include <iostream> class A{ public: A() = default; A(A& a){ std::cout << "Copy constructor." << std::endl; }; A(A&& a){ std::cout << "Move constructor." << std::endl; }; }; int main(int argc, char *argv[]){ auto a = A(A()); std::cout << "here" << std::endl; auto b = A(a); return 0; }
原本认为auto a = A(A());应该调用移动构造函数(因为A()是右值),但实际输出为:
[amirreza@localhost tmp]$ ./a.out here Copy constructor.
使用gcc 10.3.1版本,未显式指定C++版本,想知道假设是否错误。
原因分析与解决方法
1. 拷贝构造函数的定义问题
你的拷贝构造函数参数是A&(非const左值引用),不符合C++标准中拷贝构造函数的常规定义(标准拷贝构造参数应为const A&)。这种写法会干扰编译器对重载函数的匹配逻辑,同时也会导致一些右值场景下的意外行为。
2. 返回值优化(RVO)与C++17强制省略
gcc 10.3.1默认启用C17标准,在C17中,auto a = A(A());属于强制拷贝省略场景:编译器会直接在a的内存位置构造对象,完全跳过临时对象的创建、移动或拷贝步骤,因此不会触发移动构造函数的调用,自然看不到对应的日志输出。
即使在C++11/14中,编译器也会默认开启返回值优化(RVO),同样会省略移动/拷贝过程,除非显式禁用优化。
3. 验证移动构造函数的正确方式
如果想要观察到移动构造函数的调用,可以做以下调整:
- 修改拷贝构造函数为标准形式:
A(const A& a){ std::cout << "Copy constructor." << std::endl; }; - 编译时禁用返回值优化,并指定C++版本,比如:
g++ -std=c++17 -fno-elide-constructors your_code.cpp -o a.out - 或者用更直接的方式触发移动,比如使用
std::move转换左值为右值:A temp; auto a = std::move(temp); // 这里会调用移动构造函数
4. 关于auto b = A(a);的行为
这里a是左值,所以编译器会匹配参数为A&的拷贝构造函数,这部分行为是符合预期的。
内容的提问来源于stack exchange,提问作者Amir reza Riahi
相关产品推荐
相关产品推荐

