为何调用拷贝构造函数而非移动构造函数?代码相关疑问咨询
咱们先看你写的这段C++代码和运行结果:
#include <bits/stdc++.h> using namespace std; class A { public: A(const A& a) noexcept { cout << "copy constructor" << endl; } A& operator=(const A& a) noexcept { cout << "copy assignment operator" << endl; } A(A&& a) noexcept { cout << "move constructor" << endl; } A& operator=(A&& a) noexcept { cout << "move assignment operator" << endl; } A() { cout << "default constructor" << endl; } }; vector<A> aList; void AddData(const A&& a) { aList.push_back(std::move(a)); } int main() { AddData(A()); return 0; }
运行输出:default constructor copy constructor
接下来逐个解答你的三个问题:
1. 是否调用了右值引用版本的push_back(T&&)?
并没有。实际调用的是push_back(const T&)这个接受const左值引用的重载版本,而非右值引用版本的push_back(T&&)。
2. 拷贝构造函数何时被调用?
拷贝构造函数是在push_back内部执行对象复制时被调用的。具体来说,vector需要在自己的内存空间里构造一个新的A对象,因为传入的参数是const左值,所以只能通过拷贝构造来创建这个新对象。
3. 为何调用的是拷贝构造函数而非移动构造函数?
问题的核心出在AddData的参数声明const A&& a上——你把右值引用声明成了const限定的右值引用。当你对这个参数调用std::move(a)时,返回的是const A&&类型,而标准库中vector::push_back(T&&)要求的是非const的右值引用,因为移动操作本质上需要修改被移动的对象(把它的资源转移走,让原对象处于可安全析构的状态)。
由于不存在push_back(const T&&)这个重载,编译器会匹配最兼容的重载,也就是push_back(const T&),这个重载会调用拷贝构造函数来复制对象。
如果把AddData的参数改成A&& a(去掉const修饰),那么std::move(a)返回的就是非const的右值引用,此时就会触发push_back(T&&)重载,进而调用移动构造函数,输出就会变成default constructor move constructor了。
内容的提问来源于stack exchange,提问作者fizzbuzz

