vincent
vincent

Reputation: 13

C++ constructor takes temporary object not working as expected

For the code below:

struct Test
{
    int size;
    Test(int a) { cout << "default" << endl; size = a; }
    Test(Test const & t) { cout << "copy" << endl; size = t.size + 1; }
    Test(Test && t) { cout << "move" << endl; size = t.size + 1; }
    ~Test() { cout << "destruct" << endl; }
};

struct Test2
{
    int size;
    Test2(int a) { cout << "default2" << endl; size = a; }
    Test2(Test const & t) { cout << "copy2" << endl; size = t.size + 1; }
    Test2(Test && t) { cout << "move2" << endl; size = t.size + 1; }
    ~Test2() { cout << "destruct2" << endl; }
};

I found the result are different as:

    Test t{3};                                       // default
    cout << t.size << endl;                          // 3
    Test move_from_temp = Test(Test(std::move(t)));  // move
    cout << move_from_temp.size << endl;             // 4
    Test t{3};                                         // default
    cout << t.size << endl;                            // 3
    Test2 move_from_temp = Test2(Test(std::move(t)));  // move, move2, destruct
    cout << move_from_temp.size << endl;               // 5

However, I expect these two should have the same results. Anyone know the reason for this?

Upvotes: 0

Views: 78

Answers (1)

Ralf Ulrich
Ralf Ulrich

Reputation: 1714

If I run your code the result is slightly different (maybe a typo in your Q?)

    Test t{3};                                       // default
    cout << t.size << endl;                          // 3
    Test move_from_temp = Test(Test(std::move(t)));  // move
    cout << move_from_temp.size << endl;             // 4

    Test t{3};                                         // default
    cout << t.size << endl;                            // 3
    Test2 move_from_temp = Test2(Test(std::move(t)));  // move, move2, destruct
    cout << move_from_temp.size << endl;               // 5

std::move is static_cast to an rvalue reference type. It does nothing, only a cast to Test && in both versions.

Based on that, in both versions you call the move constructur Test(Test&&).

In the first version you are then done because of mandatory "copy elison" https://en.cppreference.com/w/cpp/language/copy_elision

In the second version you call Test2(Test&&) which cannot be optimized away. The compiler just call the method you provided.

Upvotes: 1

Related Questions