Corylus
Corylus

Reputation: 889

copy constructor not called when returning a std::vector

I want to know what's happening here:

class Test {
public:
    Test() { std::cout << "Constructor" << std::endl; }
    Test(const Test &) { std::cout << "Copy" << std::endl; }
    Test(const Test &&) { std::cout << "Move" << std::endl; }
    ~Test() { std::cout << "Destructor" << std::endl; }
};
std::vector<Test> getTestVektor() {
    std::vector<Test> TestVektor(1);
    return TestVektor;
}
Test getTest() {
    Test TestVariable;
    return TestVariable;
}

int main() {
    {
        std::vector<Test> TestVektor = getTestVektor();
    }
    std::cout << std::endl;
    {
        Test TestVarible = getTest();
    }
    std::cout << std::endl;
    {
        std::vector<Test> TestVektor(1);
        std::vector<Test> TestVektor2 = TestVektor;
    }
    return 0;
}

compiled with VisualStudio 2012:

Constructor
Destructor

Constructor
Move
Destructor
Destructor

Constructor
Copy
Destructor
Destructor

One could explain the first case with copy elision. But that's contrary to the second case, where the move constructor was called.

Another explanation would be, that the std::vector in the function releases its contents and passes it to the second std::vector, so there is no call of the copy constructor. But the third case shows, that that's not the case.

So, what's happening here? Or is this just mazy compiler opitimization?

Upvotes: 1

Views: 357

Answers (3)

eerorika
eerorika

Reputation: 238351

One could explain the first case with copy elision.

TestVektor was move constructed from the temporary vector that was returned from getTestVektor. One, both or neither of the moves may have been elided.

But that's contrary to the second case, where the move constructor was called.

Copy/move elision is not mandatory. It could be used for both the return from getTest and copy initialization of TestVarible, but it wasn't used for one of them.

Versions of both GCC and Clang that I tested elided both.

Another explanation would be, that the std::vector in the function releases its contents and passes it to the second std::vector

That's exactly what the move constructor of std::vector does.

But the third case does copy assignment, not a move construction.

In conclusion, what's happening here is mostly explained by the move constructor of std::vector, but the second case also shows observable (lack of) side effects from copy/move elision.

Upvotes: 0

Richard Hodges
Richard Hodges

Reputation: 69882

FYI the output from clang, with -O2:

Constructor
Destructor

Constructor
Destructor

Constructor
Copy
Destructor
Destructor

Why visual studio would invoke a move in case 2 is a mystery to me. Did you disable optimisations?

Upvotes: 0

Jarod42
Jarod42

Reputation: 217293

The first case (at worst) moves the vector (so just transfers the internal pointer, without copy/move of Test).

The 3rd case makes a copy of vector, you would have to do the following to move it instead of copy:

{
    std::vector<Test> TestVektor(1);
    std::vector<Test> TestVektor2 = std::move(TestVektor);
}

Upvotes: 2

Related Questions