Suppose we have a class named AAA
that supports both copy/move:
class AAA
{
public:
AAA() = default;
~AAA() = default;
AAA(const AAA& rhs)
{
std::cout << "Copy constructor" << std::endl;
}
AAA(AAA&& rhs)
{
std::cout << "Move constructor" << std::endl;
}
};
In the following code, get_val
returns second
:
AAA get_val()
{
auto [ first, second ] = std::make_tuple(AAA{}, AAA{});
std::cout << "Returning - " << std::endl;
return second;
}
auto obj = get_val();
std::cout << "Returned - " << std::endl;
Now second
is copied, printing the following output:
...
Returning -
Copy constructor
Returned -
This is unfortunate, because my expectation of the result is either there's no call to copy constructor, or at least it's implicitly moved.
To avoid copying, I'll have to explicitly apply std::move
on it.
return std::move(second);
Then I'd receive the result of:
...
Returning -
Move constructor
Returned -
I assume that the reason RVO is not performed is that probably the compilers would see second
as a reference instead, while get_val
returns prvalue.
However why can implicit move NOT be expected either?
Using explicit std::move
on the return statement does not look intuitive in this particular case, because you generally don't want to make RVO, which is in most cases a better optimization than move, accidentally gone away.
Tested by both compilers gcc and clang with -O3
.
However why can implicit move NOT be expected either?
For the exact same reason as elision is turned off: because it's a reference, not the name of an independent object. Every use of second
is essentially equivalent to saying obj.whatever
or get<1>(obj)
(though in the latter case, we store the reference). And there's no implicit move from either of those expressions.
Structured binding is for accessing subobjects of the given object. You cannot elide returns of subobjects, nor can you implicitly move from them. Therefore, you cannot elide structured binding names nor implicitly move from them.