The first two cases are pretty clear abuse of temporaries. Based on the asker's comments they've gotten what went wrong in create1
and create2
at this point, so let's focus on create3
and why it works.
Spoiler: It doesn't.
I'm going to take a few liberties with the code to make what's happening a bit more obvious. First, we replace vector
with a simple class that lets us see construction and destruction better.
struct test
{
test()
{
std::cout << "test ctor\n";
}
~test()
{
std::cout << "test dtor\n";
}
};
Now we do something similar to Data
and make it use test
instead of vector
struct Data {
Data(const test &data = {}) : data_(data)
{
std::cout << "data ctor\n";
}
~Data()
{
std::cout << "data dtor\n";
}
const test &data_;
void forceuse() // just to make sure the compiler doesn't get any bright ideas about
// optimizing Data out before we're through with it.
{
std::cout << "Using data\n";
}
};
And we add a bit of extra diagnostic to create3
and once again replace vector
with test
Data create3(const test &data = {}) {
std::cout << "in create3\n";
return Data(data); // good
}
and do the same to main
int main() {
{ // change the scope of data3 so that we can put a breakpoint at the end of main
// and watch all of the fun
std::cout << "calling create3\n";
auto data3 = create3(); // ok
std::cout << "returned from create3\n";
data3.forceuse();
}
}
Output of this is
calling create3
test ctor
in create3
data ctor
test dtor
returned from create3
Using data
data dtor
test
is created during the call to create3
and destroyed on exit from create3
. It is not alive in main
. If it seems to be alive in main
, that's just dumb bad luck. Your friend and mine Undefined Behaviour being a jerk. Again.
test
is created before Data
, but also destroyed before Data
leaving Data
in a bad state.
Here's the above code all nicely assembled and run in an online IDE: https://ideone.com/XY30XH