If you read code like
auto&& var = foo();
where foo is any function returning by value of type T. Then var is an lvalue of type rvalue reference to T. But what does this imply for var? Does it mean, we are allowed to steal the resources of var? Are there any reasonable situations when you should use auto&& to tell the reader of your code something like you do when you return a unique_ptr<> to tell that you have exclusive ownership? And what about for example T&& when T is of class type?
I just want to understand, if there are any other use cases of auto&& than those in template programming; like the ones discussed in the examples in this article Universal References by Scott Meyers.
By using auto&& var = <initializer> you are saying: I will accept any initializer regardless of whether it is an lvalue or rvalue expression and I will preserve its constness. This is typically used for forwarding (usually with T&&). The reason this works is because a "universal reference", auto&& or T&&, will bind to anything.
You might say, well why not just use a const auto& because that will also bind to anything? The problem with using a const reference is that it's const! You won't be able to later bind it to any non-const references or invoke any member functions that are not marked const.
As an example, imagine that you want to get a std::vector, take an iterator to its first element and modify the value pointed to by that iterator in some way:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
This code will compile just fine regardless of the initializer expression. The alternatives to auto&& fail in the following ways:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
So for this, auto&& works perfectly! An example of using auto&& like this is in a range-based for loop. See my other question for more details.
If you then use std::forward on your auto&& reference to preserve the fact that it was originally either an lvalue or an rvalue, your code says: Now that I've got your object from either an lvalue or rvalue expression, I want to preserve whichever valueness it originally had so I can use it most efficiently - this might invalidate it. As in:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
This allows use_it_elsewhere to rip its guts out for the sake of performance (avoiding copies) when the original initializer was a modifiable rvalue.
What does this mean as to whether we can or when we can steal resources from var? Well since the auto&& will bind to anything, we cannot possibly try to rip out vars guts ourselves - it may very well be an lvalue or even const. We can however std::forward it to other functions that may totally ravage its insides. As soon as we do this, we should consider var to be in an invalid state.
Now let's apply this to the case of auto&& var = foo();, as given in your question, where foo returns a T by value. In this case we know for sure that the type of var will be deduced as T&&. Since we know for certain that it's an rvalue, we don't need std::forward's permission to steal its resources. In this specific case, knowing that foo returns by value, the reader should just read it as: I'm taking an rvalue reference to the temporary returned from foo, so I can happily move from it.
As an addendum, I think it's worth mentioning when an expression like some_expression_that_may_be_rvalue_or_lvalue might turn up, other than a "well your code might change" situation. So here's a contrived example:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
Here, get_vector<T>() is that lovely expression that could be either an lvalue or rvalue depending on the generic type T. We essentially change the return type of get_vector through the template parameter of foo.
When we call foo<std::vector<int>>, get_vector will return global_vec by value, which gives an rvalue expression. Alternatively, when we call foo<std::vector<int>&>, get_vector will return global_vec by reference, resulting in an lvalue expression.
If we do:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
We get the following output, as expected:
2
1
2
2
If you were to change the auto&& in the code to any of auto, auto&, const auto&, or const auto&& then we won't get the result we want.
An alternative way to change program logic based on whether your auto&& reference is initialised with an lvalue or rvalue expression is to use type traits:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
First, I recommend reading this answer of mine as a side-read for a step-by-step explanation on how template argument deduction for universal references works.
Does it mean, we are allowed to steal the resources of var?
Not necessarily. What if foo() all of a sudden returned a reference, or you changed the call but forgot to update the use of var? Or if you're in generic code and the return type of foo() might change depending on your parameters?
Think of auto&& to be exactly the same as the T&& in template<class T> void f(T&& v);, because it's (nearly†) exactly that. What do you do with universal references in functions, when you need to pass them along or use them in any way? You use std::forward<T>(v) to get the original value category back. If it was an lvalue before being passed to your function, it stays an lvalue after being passed through std::forward. If it was an rvalue, it will become an rvalue again (remember, a named rvalue reference is an lvalue).
So, how do you use var correctly in a generic fashion? Use std::forward<decltype(var)>(var). This will work exactly the same as the std::forward<T>(v) in the function template above. If var is a T&&, you'll get an rvalue back, and if it is T&, you'll get an lvalue back.
So, back on topic: What do auto&& v = f(); and std::forward<decltype(v)>(v) in a codebase tell us? They tell us that v will be acquired and passed on in the most efficient way. Remember, though, that after having forwarded such a variable, it's possible that it's moved-from, so it'd be incorrect use it further without resetting it.
Personally, I use auto&& in generic code when I need a modifyable variable. Perfect-forwarding an rvalue is modifying, since the move operation potentially steals its guts. If I just want to be lazy (i.e., not spell the type name even if I know it) and don't need to modify (e.g., when just printing elements of a range), I'll stick to auto const&.
† auto is in so far different that auto v = {1,2,3}; will make v an std::initializer_list, whilst f({1,2,3}) will be a deduction failure.
Consider some type T which has a move constructor, and assume
T t( foo() );
uses that move constructor.
Now, let's use an intermediate reference to capture the return from foo:
auto const &ref = foo();
this rules out use of the move constructor, so the return value will have to be copied instead of moved (even if we use std::move here, we can't actually move through a const ref)
T t(std::move(ref)); // invokes T::T(T const&)
However, if we use
auto &&rvref = foo();
// ...
T t(std::move(rvref)); // invokes T::T(T &&)
the move constructor is still available.
And to address your other questions:
... Are there any reasonable situations when you should use auto&& to tell the reader of your code something ...
The first thing, as Xeo says, is essentially I'm passing X as efficiently as possible, whatever type X is. So, seeing code which uses auto&& internally should communicate that it will use move semantics internally where appropriate.
... like you do when you return a unique_ptr<> to tell that you have exclusive ownership ...
When a function template takes an argument of type T&&, it's saying it may move the object you pass in. Returning unique_ptr explicitly gives ownership to the caller; accepting T&& may remove ownership from the caller (if a move ctor exists, etc.).
The auto && syntax uses two new features of C++11:
The auto part lets the compiler deduce the type based on the context (the return value in this case). This is without any reference qualifications (allowing you to specify whether you want T, T & or T && for a deduced type T).
The && is the new move semantics. A type supporting move semantics implements a constructor T(T && other) that optimally moves the content in the new type. This allows an object to swap the internal representation instead of performing a deep copy.
This allows you to have something like:
std::vector<std::string> foo();
So:
auto var = foo();
will perform a copy of the returned vector (expensive), but:
auto &&var = foo();
will swap the internal representation of the vector (the vector from foo and the empty vector from var), so will be faster.
This is used in the new for-loop syntax:
for (auto &item : foo())
std::cout << item << std::endl;
Where the for-loop is holding an auto && to the return value from foo and item is a reference to each value in foo.
Related
When I generate a new thread (std::thread) with a function the arguments of that function
are by value - not by reference.
So if I define that function with a reference argument (int& nArg)
my compiler (mingw 4.9.2) outputs an error (in compilian-suaeli something like
"missing copy constructor" I guess ;-)
But if I make that reference argument const (const int& nArg) it does not complain.
Can somebody explain please?
If you want to pass reference, you have to wrap it into std::reference_wrapper thanks to std::ref. Like:
#include <functional>
#include <thread>
void my_function(int&);
int main()
{
int my_var = 0;
std::thread t(&my_function, std::ref(my_var));
// ...
t.join();
}
std::thread's arguments are used once.
In effect, it stores them in a std::tuple<Ts...> tup. Then it does a f( std::get<Is>(std::move(tup))...).
Passing std::get an rvalue tuple means that it is free to take the state from a value or rvalue reference field in the tuple. Without the tuple being an rvalue, it instead gives a reference to it.
Unless you use reference_wrapper (ie, std::ref/std::cref), the values you pass to std::thread are stored as values in the std::tuple. Which means the function you call is passed an rvalue to the value in the std::tuple.
rvalues can bind to const& but not to &.
Now, the std::tuple above is an implementation detail, an imagined implementation of std::thread. The wording in the standard is more obtuse.
Why does the standard say this happens? In general, you should not bind a & parameter to a value which will be immediately discarded. The function thinks that it is modifying something that the caller can see; if the value will be immediately discarded, this is usually an error on the part of the caller.
const& parameters, on the other hand, do bind to values that will be immediately discarded, because we use them for efficiency purposes not just for reference purposes.
Or, roughly, because
const int& x = 7;
is legal
int& x = 7;
is not. The first is a const& to a logically discarded object (it isn't due to reference lifetime extension, but it is logically a temporary).
What is the point of defining a local variable to be an rvalue reference or a forwarding (universal) reference? As far as I understand, any variable that has a name is an lvalue and will be treated as such moving forward.
Example:
Widget&& w1 = getWidget();
auto&& w2 = getWidget();
w1 and w2 are both lvalues, and will be treated as such if they're passed as arguments later on. Their decltype is probably not, but what difference does this make? Why would anyone need to define variables that way?
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();
}
As far as I know, there is no real, aka widely used purpose to define a local rvalue reference, since their nature, to not to bind to lvalues, is only helpful in overloading and deduction, so to define them as parameters of a function.
One could use them, to bind them to temporary values like
int &&rref = 5*2;
but since almost all compilers are optimizing the expression
int i = 5*2;
there is no real need, in therm of performance or avoid copying.
One example could be an array
template<class T, int N> using raw_array = T[N];
then
auto && nums = raw_array<int,4>{101, 102, 103, 104};
this allows the temporary to be used as if it was an ordinary array.
A variable declared auto&& will follow perfect forwarding rules. Try it for yourself. This is even how perfect forwarding is done with lambdas in c++14.
const Type& fun1();
Type&& fun2();
auto&& t1 = fun1(); // works
auto&& t2 = fun2(); // works too
if I have a function:
Foo& Bar()
{
return /// do something to create a non-temp Foo here and return a reference to it
}
why is this:
auto x = Bar(); /// probably calls copy ctor - haven't checked
not the same as this?
auto &x = Bar(); /// actually get a reference here
(Actually, I'd expect the second version to get a reference to a reference, which makes little sense.)
If I explicitly specified the type of x as a value or a reference, I'll get what I expect (of course). I would expect, though, that auto would compile to the return type of Bar(), which, in this case, is a reference.
Is there an implicit cast between Foo and Foo& that comes into play here?
(Spec references accepted, though I'm getting tired of reading committee-speak.)
(Second use of time machine will be making C++ pass by reference by default. With a #pragma compatibility trigger for compiling C code. ARGH.)
The type deduction for auto works exactly the same as for templates:
when you deduce auto you will get a value type.
when you deduce auto& you wil get a non-const reference type
when you deduce const auto& you will get a const reference
when you deduce auto&& you will get
a non-const reference if you assign a non-const reference
a const reference if you assign a const reference
a value when you assign a temporary
Taken directly from Herb Sutter's blog post:
auto means “take exactly the type on the right-hand side, but strip off top-level const/volatile and &/&&.”
Given the following function template from "The C++ Programming language 4th edition":
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
I find it difficult to understand what that actually does,
a is definitely an rvalue and therefore the make_unique function
seem to allocate its content on the heap and holding that address in a unique_ptr so we won't have to worry about deleting it. but, what does the standard library forward function does? (I guess it has something to do with a being rvalue) I tried reading at C++ documentation but I don't seem to understand that properly.
would love to get a good explanation from a more experienced C++ programmer.
thanks!
Hmmm... I'm pretty sure this isn't given as a work-around implementation of the future std::make_unique, but anyway, what the function does is pretty easy to understand, though it requires you to have prior knowledge of new C++11 features.
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
First of all make_unique is a function template, I really hope you already know that, as the following would require that you have at least the most basic knowledge on what templates does and how templates work.
Now to the non-trivial parts. A && a there is a function parameter. Specifically, a is the function parameter whose type is A&& which is an r-value reference. With its type being a template type parameter, we can deduce its type from whatever the caller passes as an argument to a. Whenever we have r-value reference and argument type deduction, special deduction rules and reference collapsing kicks-in and we have a so-called "universal reference" which is particularly useful for perfect forwarding functions.
Whenever we have a universal reference (a in our case), we will almost always want to preserve its original "l-valueness" or "r-valueness" whenever we want to use them. To have this kind of behavior, we should almost always use std::forward (std::forward<A>(a)). By using std::forward, a variable originally passed as an l-value remains an l-value and a variable originally passed as an r-value remains an r-value.
After that, things are just simple
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
Notice the use of the braces. Instead of using parentheses, it is using C++11's uniform initialization syntax of calling constructors. With new TT{ i, std::forward<A>(a) }, you are dynamically allocating an object of type TT with the given parameters inside the braces. With unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};, you are creating a unique_ptr<TT> whose parameter is the one returned by the dynamic allocation. The unique_ptr<TT> object now then returned from the function.
Due to template argument deduction and reference collapsing rules you cannot know if a is a rvalue reference or a lvalue reference. std::forward passes the argument to the TT contrustor exactly as it was passed to make_unique. Scott Meyers calls A&& a universal reference, because it can be a lvalue ref or an rvalue ref, depended on what is passed to make_unique.
If you pass an rvalue Foo to make_unique, std::forward passes an rvalue reference.
If you pass an lvalue Foo to make_unique, std::forward passes an lvalue reference.
make_unique(1, Foo()); // make_unique(int, A&&) -> rvalue ref
Foo f;
make_unique(1, f); // make_unique(int, A&&&) -> make_unique(int, A&) -> lvalue ref
make_unique(1, std::move(f)); // make_unique(int, A&&&&) -> make_unique(int, A&&) -> rvalue ref
For move enabled classes is there a difference between this two?
struct Foo {
typedef std::vector<std::string> Vectype;
Vectype m_vec;
//this or
void bar(Vectype&& vec)
{
m_vec = std::move(vec);
}
//that
void bar(Vectype vec)
{
m_vec = std::move(vec);
}
};
int main()
{
Vectype myvec{"alpha","beta","gamma"};
Foo fool;
fool.bar(std::move(myvec));
}
My understanding is that if you use a lvalue myvec you also required to introduce const
Vectype& version of Foo::bar() since Vectype&& won't bind. That's aside, in the rvalue case, Foo::bar(Vectype) will construct the vector using the move constructor or better yet elide the copy all together seeing vec is an rvalue (would it?). So is there a compelling reason to not to prefer by value declaration instead of lvalue and rvalue overloads?
(Consider I need to copy the vector to the member variable in any case.)
The pass-by-value version allows an lvalue argument and makes a copy of it. The rvalue-reference version can't be called with an lvalue argument.
Use const Type& when you don't need to change or copy the argument at all, use pass-by-value when you want a modifiable value but don't care how you get it, and use Type& and Type&& overloads when you want something slightly different to happen depending on the context.
The pass-by-value function is sufficient (and equivalent), as long as the argument type has an efficient move constructor, which is true in this case for std::vector.
Otherwise, using the pass-by-value function may introduce an extra copy-construction compared to using the pass-by-rvalue-ref function.
See the answer https://stackoverflow.com/a/7587151/1190077 to the related question Do I need to overload methods accepting const lvalue reference for rvalue references explicitly? .
Yes, the first one (Vectype&& vec) won't accept a const object or simply lvalue.
If you want to save the object inside like you do, it's best to copy(or move if you pass an rvalue) in the interface and then move, just like you did in your second example.