Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper named temporaries and rvalue-reference/move

Tags:

c++

c++11

Prior to C++11, and as a standard programming idiom, temporaries are often assigned to variables to make the code cleaner. For small types a copy is typically made, and for larger types perhaps a reference, such as:

int a = int_func();
T const & obj = obj_func();
some_func( a, obj );

Now, compare this to the inlined form:

some_func( int_func(), obj_func() );

Prior to C++11 this had nearly identical semantic meaning. With the introduction of rvalue-reference and move semantics the above are now entirely different. In particular, by forcing obj to type T const & you have removed the ability to use a move constructor, whereas the inline form the type can be a T&& instead.

Given that the first is a common paradigm, is there anything in the standard that would allow an optimizer to use a move constructor in the first case? That is, could somehow the compiler ignore the binding to a T const & and instead treat it as a T&&, or, as I suspect, would this violate the rules of the abstract machine?

Second part of the question, to do this correctly in C++11 (without eliminating named temporaries) we need to somehow declare a proper rvalue-reference. We can also use the auto keyword. So, what is the proper way to do this? My guess is:

auto&& obj = obj_func();
like image 689
edA-qa mort-ora-y Avatar asked Dec 10 '22 05:12

edA-qa mort-ora-y


2 Answers

Part 1:

The compiler is not allowed to implicilty transform obj into a non-const rvalue and thus use a move constructor when calling some_func.

Part 2:

auto&& obj = obj_func();

This will create a non-const reference to the temporary, but it will not be implicitly moved from when calling some_func because obj is an lvalue. To transform it to an rvalue you should use std::move at the call site:

some_func( a, std::move(obj) );
like image 131
Howard Hinnant Avatar answered Dec 11 '22 19:12

Howard Hinnant


I doubt this could be optimized, since it's not clear whether you will use the temporary again or not:

Foo x = get_foo(); // using best-possible constructor and (N)RVO anyway
do_something(x);
do_something_else(x);
//...

If you're really keen on exploiting move semantics somewhere (but be sure to profile first to see that this really matters), you can make this explicit with move:

Foo y = get_foo();
do_oneoff_thing(std::move(y));  // now y is no longer valid!

I'd say that if something is eligible for moving, then you might as well do the inlining yourself and do without the extra local variable. After all, what good is such a temporary variable if it is only used once? The only scenario that comes to mind is if the last use of the local variable can exploit move semantics, so you could add std::move to the final appearance. That sounds like a maintenance hazard though, and you'd really need a compelling reason to write that.

like image 43
Kerrek SB Avatar answered Dec 11 '22 18:12

Kerrek SB