Search code examples
c++copytuplesmovervalue

Why are constructed objects, in tuple initialization, copied?


I have a situation which I cannot wrap my head around. I define a non-copy-able struct and want to in-place construct it in a tuple. If I do so, it is copied. I thought the problem may be std::make_tuple, but it isn't.

If instead I construct the object, then move it in the tuple, things work as expected.

Demonstration (I use std::exit to prevent the output of normal destruction).

#include <tuple>
#include <iostream>
#include <cstdlib>

struct MoveMe {
    MoveMe(size_t s) : size(s) {}
    MoveMe(MoveMe&&) = default;
    MoveMe(const MoveMe&) = delete;

    MoveMe& operator=(MoveMe&&) = default;
    MoveMe& operator=(const MoveMe&) = delete;

    ~MoveMe() {
        std::cout << "Destroyed" << std::endl;
    }

    size_t size = 0;
};

int main(int, char**) {
    std::cout << "Constructed in-place." << std::endl;
    auto tuple = std::make_tuple(MoveMe{10});

    std::cout << std::endl << "Other tests." << std::endl;
    std::tuple<MoveMe> tuple2 = std::tuple<MoveMe>(MoveMe{10});

    std::cout << std::endl << "Moved." << std::endl;
    MoveMe obj{10};
    auto tuple3 = std::make_tuple(std::move(obj));

    std::exit(0);
    // return 0;
}

Outputs

Constructed in-place.

Destroyed

Other tests.

Destroyed

Moved.

Any ideas why that is? My understanding of rvalues is basic, so my guess is I'm missing something obvious. Thank you.


Solution

  • It isn't copied it is still moved.

    In your case, you're creating a temporary (MoveMe{10}) that is then used to move construct the instance of MoveMe in the tuple. After the instance of MoveMe in the tuple is move constructed, the temporary that was moved is destroyed.

    You can directly construct the MoveMe object in the tuple by forwarding the arguments

    std::cout << "Directly Constructed" << std::endl;
    auto tuple = std::tuple<MoveMe>(10);
    

    Which will not result in a temporary being created then destroyed.