If you have a function returning a temporary which cannot be moved.
Foo some_function();
auto&& f = some_function();
This is legal. auto f = some_function();
will either copy (which could be expensive), or fail to compile (if the class also cannot be copied).
In general, auto&&
deduces down to either an r or lvalue reference depending on what it is initialized with, and if initialized with a temporary extends its lifetime while giving you access to it as an lvalue.
A classic use is in the 'just-loop' pattern:
for( auto&& x : some_range )
where there is actually a auto&& x = *it;
in the code generated.
You cannot bind a non-constant lvalue reference to a temporary, so your other choice is Widget const&
, which doesn't let you modify the temporary during its lifetime.
This technique is also useful to decompose a complex expression and see what is going on. So long as you aren't working with extremely fragile expression templates, you can take the expression a+b+c*d
and turn it into
auto&& c_times_d = d*d;
auto&& b_plus_c_times_d = b + decltype(c_times_d)c_times_d;
auto&& c_plus_b_plus_c_times_d = c + decltype(b_plus_c_times_d)b_plus_c_times_d;
and now you have access to the temporary objects whose lifetime is extended, and you can easily step through the code, or introduce extra steps between steps in a complex expression: and this happens mechanically.
The concern about fragile expression templates only holds if you fail to bind every sub-expression. (Note that using ->
can generate a myriad of sub-expressions you might not notice.)
I use auto&&
when I want to say "I'm storing the return value of some function, as is, without making a copy", and the type of the expression is not important. auto
is when I want to make a local copy.
In generic code it can be highly useful.
Foo const& a(Foo*);
Bar a(Bar*);
template<class T>
auto do_stuff( T*ptr ) {
auto&& x = a(ptr);
}
here if you pass a Bar*
it stores the temporary, but if you pass a Foo*
to do_stuff
it stores the const&
.
It does the least it can.
Here is an example of a function returning a non-movable non-copyable object, and how auto&&
lets you store it. It is otherwise useless, but it shows how it works:
struct Foo {
Foo(&&)=delete;
Foo(int x) { std::cout << "Foo " << x << " constructed\n";
};
Foo test() {
return {3};
}
int main() {
auto&& f = test();
}