4
#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.

5
  • 5
    You are copying the temporary Car object that you constructed here Car(1). If you want to avoid this copy use emplace_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. Commented Mar 29, 2025 at 20:07
  • I'm not sure, but maybe because vector contains Car objects and not pointers or references on Car objects. Because of that, every time you push element into vector, it will make copy of that element and hence call copy constructor. Commented Mar 29, 2025 at 20:07
  • 1
    See std::vector::push_back() and it should be clear that it copies its argument. Commented Mar 29, 2025 at 20:13
  • Btw; this->weight=other.weight; can simply be weight=other.weight;. There's no need to explicitly dereference this. Commented Mar 29, 2025 at 20:15
  • 1
    @JesperJuhl or even just using a member initializer list. Commented Mar 30, 2025 at 0:35

3 Answers 3

10

You 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. This may be avoided by judicious use of reserve.

Sign up to request clarification or add additional context in comments.

Comments

3

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:

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!

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.