admin管理员组文章数量:1355694
#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;
}
In the code above, somehow when I push Car(1) onto vec, the copy constructor in the Car class is called. Why is the copy constructor called? I'm not copying anything here.
#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;
}
In the code above, somehow when I push Car(1) onto vec, the copy constructor in the Car class is called. Why is the copy constructor called? I'm not copying anything here.
Share Improve this question edited Mar 29 at 20:33 Chris 36.9k6 gold badges33 silver badges55 bronze badges asked Mar 29 at 19:59 simply lemonsimply lemon 1311 silver badge5 bronze badges 5 |3 Answers
Reset to default 10You are copying since your Car
struct does not implement move semantics. The copy is from the temporary you built with Car(1)
to the object created in the vector. Using emplace_back
to directly construct the object in the vector without the temporary object will prevent this.
#include <iostream>
#include <vector>
struct Car {
int weight;
Car(int weight) : weight(weight) { }
Car(const Car& other) : weight(other.weight) {
std::cout << "copied!\n";
}
};
int main() {
std::vector<Car> vec;
vec.emplace_back(1);
}
Note that even using emplace_back
you may still see copy constructors called if the vector has to reallocate.
A bit of elaboration on the topic due to the fact that you labeled your question with move-semantics
tag. My assumption is that you expected your code to expose those semantics here and hence your surprise here.
As you probably know, in C++ 11 all containers have been updated with support for moving objects into their storage instead of copying. Now, for most functions designed to insert a new value into a container we have something like:
void push_back( const T& value );
void push_back( T&& value );
Which of these is selected, depends on the parameter type and the ability of T to be copy- and/or move-constructed. And this is where your code gets it wrong.
Indeed, vec.push_back(Car(1));
should move-insert the value to the container (Car(1)
is a temporary bound to an rvalue, so push_back( T&& value );
is definitely the matching overload). Why this is not happenening is because your class does not provide move semantics.
By default, the compiler generates all set of constructors, assignment operators and a destructor (depends on the members stored inside a class and their copy/move abilities), but when a user-defined versions appear, this is no longer the case.
The wording you should be looking at can be found here, for example and goes as follows:
Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type, and all of the following is true:
there are no user-declared copy constructors;
there are no user-declared copy assignment operators;
there are no user-declared move assignment operators;
there is no user-declared destructor.
Then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature
T::T(T&&)
.
And there you have it - since you defined your own copy constructor, the first requirement is not met, so you need to write your own move constructor if you want it (and you most definitely should do the same for relevant assignment operators as well).
If you haven't come accross terms "Rule of Three (Five since C++ 11)", take a look here.
After this quite long elaborate for this simple problem, let's finally fix your code:
class Car{
public:
int weight;
Car(int weight): weight(weight){
};
Car(Car&& other){
std::cout<<"moved!"<<std::endl;
this->weight=other.weight;
}
Car(const Car& other){
std::cout<<"copied!"<<std::endl;
this->weight=other.weight;
}
};
We can then run your code and observe the right result you expected:
Success #stdin #stdout 0.01s 5288KB
moved!
A temporary `Car(1)` is being pushed into the vector, the copy constructor is called. C++ adds the item to the vector using the copy constructor because your class lacks a move constructor. The vector must create a copy in order to store it, even though it appears that you are not copying.
本文标签: cCopy constructor called when pushing object onto stdvectorStack Overflow
版权声明:本文标题:c++ - Copy constructor called when pushing object onto std::vector - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744005034a2574544.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
Car
object that you constructed hereCar(1)
. If you want to avoid this copy useemplace_back
, e.g.vec.emplace_back(1)
. emplace_back will call any available constructor and contruct the object being added directly in the vector, without any copying. – john Commented Mar 29 at 20:07Car
objects and not pointers or references onCar
objects. Because of that, every time you push element into vector, it will make copy of that element and hence call copy constructor. – Milos Stojanovic Commented Mar 29 at 20:07this->weight=other.weight;
can simply beweight=other.weight;
. There's no need to explicitly dereferencethis
. – Jesper Juhl Commented Mar 29 at 20:15