开发者

Proper named temporaries and rvalue-reference/move

开发者 https://www.devze.com 2023-04-10 07:14 出处:网络
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 refe

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 p开发者_开发百科art 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();


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) );


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.


I don't think that the const & binding to a temporary to extend the lifetime is so common. As a matter of fact, in C++03, in many cases the copy can be elided by just passing by value and calling the function in what you call the inlined form: some_func( int_func(), obj_func() ), so the same problem that you are noticing in C++11 would occur in C++03 (in a slightly different way)

As of the const reference binding, in the event that obj_func() returns an object of type T, the above code is just a cumbersome way of doing T obj = obj_func(); that offers no advantage other than making people wonder why that was needed.

If obj_func() returns a type T1 derived from T, that trick enables you to ignore the exact return type, but that can also be achieved by using the auto keyword, so in either case, what you have is a local named variable obj.

The proper way to pass the obj into the function --if you are finished with it, and the function can move the value from obj to an internal object, would be to actually move:

some_func( a, std::move(obj) );
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号