#include <iostream>
#include <vector>
class Car{
public:
int weight;
Car(int weight): weight(weight){
};
Car(const Car& other){
std::cout<<"copied!"<<std::endl;
this->weight=other.weight;
}
};
int main() {
std::vector<Car> vec;
vec.push_back(Car(1));
return 0;
}
在上面的代码中,当我将 Car(1) 推送到 vec 上时,不知何故 Car 类中的复制构造函数被调用。为什么要调用复制构造函数?我这里没有复制任何东西。
由于您的结构未实现移动语义,因此您正在进行
Car
复制。复制是从您构建的临时对象Car(1)
到在向量中创建的对象。使用emplace_back
直接在向量中构造对象而不使用临时对象将阻止这种情况。请注意,
emplace_back
如果向量必须重新分配,即使使用,您仍可能会看到调用复制构造函数。由于您在问题中使用了
move-semantics
标签,因此需要对主题进行一些详细说明。我的假设是您希望您的代码在此处公开这些语义,因此您在这里感到惊讶。您可能知道,在 C++ 11 中,所有容器都已更新,支持将对象移动到其存储中而不是复制。现在,对于大多数旨在将新值插入容器的函数,我们有类似以下内容:
选择哪一个取决于参数类型以及 T 的复制和/或移动构造能力。这就是你的代码出错的地方。
确实,
vec.push_back(Car(1));
应该将值移动插入到容器中(Car(1)
是与右值的临时绑定,因此push_back( T&& value );
肯定是匹配的重载)。之所以没有发生这种情况,是因为您的类不提供移动语义。默认情况下,编译器会生成所有构造函数、赋值运算符和析构函数(取决于类中存储的成员及其复制/移动能力),但是当出现用户定义的版本时,情况就不再如此。
您应该查看的措辞可以在这里找到,例如如下:
现在你已经有了它 - 因为你定义了自己的复制构造函数,所以第一个要求没有得到满足,所以如果你想要它,你需要编写自己的移动构造函数(并且你绝对应该对相关的赋值运算符执行同样的事情)。
如果你还没有遇到过术语“三法则(自 C++ 11 起为五法则)”,请看这里。
经过对这个简单问题的相当长时间的阐述之后,让我们最终修复您的代码:
然后我们可以运行您的代码并观察您预期的正确结果:
临时的“Car(1)”被推入向量,复制构造函数被调用。C++ 使用复制构造函数将项目添加到向量中,因为您的类缺少移动构造函数。即使看起来您没有复制,向量也必须创建一个副本才能存储它。