auto&& mytup = std::make_tuple(9,1,"hello");
std::get<0>(mytup) = 42;
cout << std::get<0>(mytup) << endl;
- Is there a copy/move involved (without RVO) when returning from make_tuple?
- Is it causing undefined behavior?
- I can both read write the universal reference. Can
auto&& var = func()
be used always instead ofauto var = func()
so that there is no copy/move?
Yes. Any return from a function that does not return a reference type may involve a copy/move. Eliding that is what RVO is about. The object that your reference is bound to needs to be initialized somehow.
No. why should it? The lifetime of a temporary/prvalue bound to a reference is determined by the scope of the reference.
If func() does not return a reference type, there should not be any difference in efficiency (nor behaviour)
between
auto&& var = func();
and
auto var = func();
In both cases an object with lifetime to the end of the containing block is constructed. In one case it has its own name, in the other it is named via a reference. In both cases the name can be used as an lvalue. RVO can be equally well applied in either case.
Some compilers might optimize better for a local object than for a reference, even though in the current case the reference-to-temporary is really no different from a local object.
If func()
might return a reference, things are much different - in that case you must decide whether you want to copy/move or not.
It's only ever problematic in the case where the initializer is a function call that returns a short-lived rvalue reference. With less words and more code:
// Fine; lifetime extension applies!
auto&& ref = 42;
auto id = [](int&& i) -> int&& { return std::move(i); };
auto&& uhoh = id(42);
// uhoh is now a stale reference; can't touch it!
In contrast, auto uhoh = id(42);
would have worked fine.
In your case, because std::make_tuple
returns a value and not an rvalue reference there is no problem.
I'm of the opinion that the real danger is from those functions and function templates with rvalue reference parameters and that return an rvalue reference to either those of some subobjects which lifetimes depend on those. (That being said, something as simple as auto&& ref = std::move(42);
exhibits the problem!)
The situation is not entirely new from C++11, consider: T const& ref = bar(T_factory());
.
There is a specific rule in C++ (even before C++11) that says that if you bind a reference to a temporary the lifetime of the temporary will be extended to the lifetime of the reference.
A simpler case is:
int foo () {
return 42;
}
int bar () {
const int& x = foo();
return x; // Here is safe to use x
}
The result of evaluating the call to make_tuple
is a prvalue temporary of a tuple
instantiation. The auto
type-specifier will be inferred as the same tuple
instantiation (7.1.6.4p6), so mytup
is of type tuple<...> &&
. The prvalue temporary is then lifetime-extended by the reference mytup
(12.2p5).
- Because the temporary is the function's return value, there is no copy/move involved (There may still be RVO within
make_tuple
). - The behaviour is fully defined.
- For almost all cases,
mytup
will be treated as an lvalue even though its type is an rvalue reference. However there is no point usingauto &&
as all sensible compilers will elide the copy/move from the temporary.
To clarify, about the only way to have mytup
treated as an rvalue is to use std::forward<decltype(mytup)>(mytup)
, as in What does auto&& tell us?; however if you know the type of mytup
(tuple<...>
in this case) then you may as well use std::move
.
来源:https://stackoverflow.com/questions/14849107/when-not-to-use-auto