Home > Blockchain >  Is it possible to pass an object as a parameter to a function without copying it?
Is it possible to pass an object as a parameter to a function without copying it?

Time:06-06

Look at this code

#include <iostream>
#include <vector>

class Entity {
public:
    Entity(int x, int y) : x(x), y(y) {std::cout << "DEFAULT\n";}

    Entity(const Entity& that) {
        std::cout << "COPY\n";
        this->x = that.x;
        this->y = that.y;
    }

    Entity(Entity&& that) {
        std::cout << "MOVE\n";
        this->x = that.x;
        this->y = that.y;
    }

private:
    int x, y;

};

class List {
public:
    List(){vec.reserve(25);}

    void Add(Entity &&en) {
        vec.emplace_back(std::move(en)); //invokes the move constructor
    }
    
    void Add(int x, int y) {
        vec.emplace_back(x, y);
    }

private:
    std::vector<Entity> vec;

};

int main() {
    List L;
    L.Add(Entity(10, 20));
    L.Add(100, 200);

    return 0;
}

I've defined a vector of type Entity inside the List class and provided a function Add() to access this vector. Using the Add(Entity&&) overload invokes the move constructor meanwhile using the other overload, Add(int, int) doesn't trigger anything but the default constructor of Entity.

I know why Add(int, int) doesn't invoke copy or move constructors. Because std::vector::emplace_back() utilizes the placement new operator to create the object in-place.

But I don't understand the behavior of the Add(Entity&&) function. Why is the object getting moved? Is it because it has been created outside of the stack frame of the calling function? If so and if it's possible, how to avoid moving the object?

If I'm totally wrong, please clarify it for me. Thanks in advance.

CodePudding user response:

With emplace_back you provide arguments that can be directly passed to the Entity::Entity(int,int) constructor.

When you already have an object of type Entity your only option is to pass that object to the constructor. The only 2 constructors that take a parameter of type Entity are the copy and the move constructor. The move constructor is chosen because you pass an rvalue (via std::move)

how to avoid moving the object?

If you already have the object then the only 2 options are copy or move. What else could there be? If you have the individual parameters needed to construct the object (like your int, int) then you can use emplace_back like you do in that case.

CodePudding user response:

When you call

L.Add(Entity(10, 20));

the following operations happen:

Entity(10, 20) creates a temporary
List::add is called with temporary
std::move() makes the temporary movable
vector::emplace_back is called with the temporary
vector resizes
vector calls construct_at with the temporary
construct_at calls placement new with the temporary
placement new copies/moves the temporary into place

Now the compiler elides all of that except these three things:

Entity(10, 20) creates a temporary
vector resizes
placement new copies/moves constructs the temporary

Unfortunately the compiler does not elide the copy/move construction in the new call. Unlike Entity(Entity(10, 20)), where the copy/move construct is optimized out, the new(addr) Entity(Entity(10, 20)) your code basically comes to does not collapse in the same way.

It would be nice if the compiler would (could?) optimize the extra copy/move construct away but it's not defined for new to do that.

  • Related