My understanding is that a move constructor is called if it exists when we return a local object from a function. However, I ran into a situation where the copy constructor
tNode foo()
{
tNode node;
return node;
}
and
tNode n1 = foo();
Is responsible for the output of
a: 10, tNode() is called at testCopyControl.cpp:13
a: 10, move constructor tNode() is called at testCopyControl.cpp:31
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40
a: 10, move constructor tNode() is called at testCopyControl.cpp:31
a: 10, destructor ~tNode() is called at testCopyControl.cpp:40
And what you see is the default constructor being called and then node
begin treated as an rvalue in the return statement to move it into the return value and then another move from the return value into n1
With
tNode foo2()
{
std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
return *up;
}
The behavior is different as you are not returning a function local object. *up
gives you a tNode&
so the return statement can't treat it as an rvalue. Since it is an lvalue you have to call the copy constructor to copy it into the return value. Then, like the first example, the move constructor is called to move the object from the return value into n2
.
The following code does not implicitly move the constructed object:
tNode foo2()
{
std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
return *up;
}
This is because, however obvious/intuitive it might seem to us, the compiler cannot prove that it is safe to move-from the object contained by up
. It's forced to return by copy.
You could force it to return by R-value by explicitly casting the object as such:
tNode foo2()
{
std::unique_ptr<tNode> up = std::make_unique<tNode>(20);
return std::move(*up);
}