The following C++11 code does not compile:
struct T {};
void f(T&&) { }
void g(T&& t) { f(t); }
int main()
{
g(T());
}
The correct way to do this is:
void g(T&& t) { f(move(t)); }
This is very difficult to explain in the correct natural language terminology. The parameter t seems to lose its "&&" status which it needs to have reinstated with the std::move.
What do you call the T() in g(T()) ?
What do you call the T&& in g(T&& t) ?
What do you call the t in g(T&& t) ?
What do you call the t in f(t) and f(move(t)) ?
What do you call the return value of move(t)?
What do you call the overall effect?
Which section(s) of the standard deal with this issue?
The key point is that a parameter T&& b can only bind to an rvalue, but when referred to later the expression b is an lvalue.
So the argument to the function must be an rvalue, but inside the function body the parameter is an lvalue because by then you've bound a reference to it and given it a name and it's no longer an unnamed temporary.
An expression has a type (e.g. int, string etc.) and it has a value category (e.g. lvalue or rvalue) and these two things are distinct.
A named variable which is declared as T&& b has type "rvalue reference to T" and can only be bound to an rvalue, but when you later use that reference the expression b has value category "lvalue", because it has a name and refers to some object (whatever the reference is bound to, even though that was an rvalue.) This means to pass b to another function which takes an rvalue you can't just say f(b) because b is an lvalue, so you must convert it (back) to an rvalue, via std::move(b).
All parameters are lvalues, even if their type is "rvalue reference". They have names, so you can refer to them as often as you want. If named rvalue references were rvalues, you would get surprising behavior. We do not want implicit moves from lvalues, that's why you have to explicitly write std::move.
What do you call the T() in g(T()) ?
A temporary (which is movable).
What do you call the T&& in g(T&& t) ?
T&& is an r-value reference and represent an object that can be moved.
What do you call the t in g(T&& t) ?
t is actually an l-value since you can refer to it by name.
What do you call the t in f(t) and f(move(t)) ?
l-value
l-value being converted into an r-value reference when returned by move()
What do you call the return value of move(t)?
An r-value reference
As a note; you should maybe call the struct C, and write a separate example where T is actually templetized. The code needs to be different then, because in a function template< typename T > void f( T&& t ); you cannot simply use std::move() without being very careful, since T can actually be a const&, in which case you must not use std::move() but instead use perfect forwarding with std::forward< T >( t )
What do you call the T() in g(T()) ?
That is a temporary object and an r-value.
What do you call the T&& in g(T&& t) ?
You call that a r-value reference.
The reason why
void g(T&& t) { f(t); }
doesn't work is because an r-value reference can't bind to a named object (even if that named object happens to be another r-value reference).
The parameter t does not lose its "status". Simply, the parameter t is an lvalue, even though it is an rvalue reference. Keep in mind that lvalueness and rvalueness are orthogonal concepts and apply to values as to value references (including rvalue references). Thus, an rvalue reference can be either an lvalue or an rvalue. If it has a name, as in your example, it is an lvalue. This makes the type system orthogonal and it is a good feature IMHO.
Related
Trying to understand std::move, I found this answer to another question.
Say I have this function
Object&& move(Object&& arg)
{
return static_cast<Object&&>(arg);
}
What I think I understand:
arg is an lvalue (value category).
arg is of type "rvalue ref to Object".
static_cast converts types.
arg and the return type both being of type "rvalue ref to Object", the static_cast is unnecessary.
However, the linked answer says:
Now, you might wonder: do we even need the cast? The answer is: yes, we do. The reason is simple; named rvalue reference is treated as lvalue (and implicit conversion from lvalue to rvalue reference is forbidden by standard).
I still don't understand why the static_cast is necessary given what I said above.
the static_cast is unnecessary.
It may seem so, but it is necessary. You can find out easily by attempting to write such function without the cast, as the compiler should tell you that the program is ill-formed. The function (template) returns an rvalue reference. That rvalue reference cannot be bound to an lvalue. The id-expression arg is an lvalue (as you stated) and hence the returned rvalue reference cannot be bound to it.
It might be easier to understand outside of return value context. The rules are same here:
T obj;
T&& rref0 = obj; // lvalue; not OK
T&& rref1 = static_cast<T&&>(obj); // xvalue; OK
T&& rref2 = rref1; // lvalue; not OK
T&& rref3 = static_cast<T&&>(rref1); // xvalue; OK
I have the following mental model for it (let's use int instead of Object).
Objects which have a name are "sitting on the ground". They are lvalues; you cannot convert them to rvalue references.
int do_stuff(int x, int&& y) {...} // both x and y have a name
When you do calculations, you pick objects from the ground, do your stuff in mid-air and put the result back.
x + y; // it's in mid-air
do_stuff(4, 5); // return value is in mid-air
These temporary results can be converted to rvalue references. But as soon as you "put them onto the ground", they behave as lvalues.
int&& z = x + y; // on the ground
int&& z = do_stuff(6, 7); // on the ground
I am sure it only helps in simple situations, but at least it gives some real-world analogy to how C++ works.
Your first bullet is incorrect fundamentally, arg is not a lvalue, neither it is an rvalue. Neither it's a rvalue or lvalue reference, because std::move is a template. In template context a function argument of type T&& is a forwarding reference, if T is a template-parameter. Forwarding reference becomes of type which appropriate, depending on what is T.
(and implicit conversion from lvalue to rvalue reference is forbidden by standard).
A cast is required literally because of that. Following code is incorrect, because you can't call foo(v), as v is a named object and it can be an lvalue:
void foo(int && a) { a = 5; }
int main()
{
int v;
foo(v);
std::cout << a << std::endl;
}
But if foo() is a template, it may become a function with int&, const int& and int&& arguments.
template<class T>
void foo(T && a) { a = 5; }
You would be able to call foo(v+5), where argument is a temporary, which can be bound to rvalue reference. foo will change the temporary object which stops to exist after function call. That's the exact action which move constructors usually have to do - to modify temporary object before its destructor is called.
NB: An rvalue argument would cease to exist earlier , either after its use or at end of function call.
Forwarding references are a special kind of reference syntax designed to preserve the value category of a function argument. I.e. non-template function
Object&& move(Object&& arg)
is not equal to std::move for Object, which declared something like (c++11):
template<class T>
std::remove_reference<T>::type&& move( T&& t );
In non-template function arg is an lvalue, in template it have same value category as expression used to initialize it. In template std::remove_reference<T>::type refers to T, so std::remove_reference<T>::type&& is a true rvalue reference to T - a way around T&& alternative meaning.
By analogy to function call above, if implicit conversion was possible, then it would be possible to call move constructor where copy constructor is appropriate but missing, i.e. by mistake. return static_cast<Object&&>(arg); results in initialization involving call to Object::Object(Object&&) by definition of return, return arg would call Object::Object(const Object&).
Template std::move is type-correct "wrapper" around the static_cast to facilitate "implicit" cast, to simplify code by removing repeated static_cast with explicit type from code.
Let T be an arbitrary type. Consider a function that takes a const [lvalue] reference:
void f(const T &obj);
Suppose that this function internally makes a call to another function, which has an rvalue reference overload:
void g(T &&obj);
If we pass an rvalue to f, will the rvalue reference overload of g be called, or will it fail to do so since it has been "converted"/bound to a const lvalue reference?
Similarly, if f called a function that takes an instance of T by value,
void h(T obj);
and T has a move constructor, (i.e. T(T &&);), will the move constructor be called, or will the copy constructor be called?
In conclusion, if we wanted to ensure that, when calling f on an rvalue, the rvalue reference is passed around keeping its rvalue "status", would we necessarily have to provide an rvalue reference overload for f?
Value categories are applied to expressions, not objects. obj in f is an lvalue expression, and will therefore be treated as such. Note that obj in g is also an lvalue expression; if the expression is a name for an object, then it's an lvalue.
The ability you're talking about is exactly why forwarding references exist: so that the copy/move aspects of an argument expression's value category can be preserved through function calls. f would have to become a template of the form template<typename T> void f(T&& t);, and you would have to use std::forward when passing it to g.
When you use the name of a reference as an expression, that expression is always an lvalue. No matter if it's a lvalue reference or an rvalue reference. It also doesn't matter if the reference was initialized with an lvalue or an rvalue.
int &&x = 1;
f(x); // Here `x` is lvalue.
So in void f(const T &obj) {...}, obj is always an lvalue, regardless of what you pass as an argument.
Also note that value category is determined at compile time. Since f is not a template, value category of every expression in it can't depend on arguments you pass.
Thus:
If we pass an rvalue to f, will the rvalue reference overload of g be called
No.
if f called a function that takes an instance of T by value, void h(T obj); and T has a move constructor, (i.e. T(T &&);), will the move constructor be called
No.
In conclusion, if we wanted to ensure that, when calling f on an rvalue, the rvalue reference is passed around keeping its rvalue "status", would we necessarily have to provide an rvalue reference overload for f?
Providing an overload is one option. Note that in this case you have to explicitly call std::move in the rvalue overload.
Another option is using forwarding references, as Nicol Bolas suggests:
template <typename T> void f(T &&t)
{
g(std::forward<T>(t));
}
Here, std::forward essentially acts as a 'conditional move'. It moves t if an rvalue was passed to it, and does nothing otherwise.
Consider this code:
template<typename T>
void foo(T&& param){ //In this case && is called universal reference
std:string tmp = std::forward<string>(param);
}
My question is if universal reference type can be deduced why do I still need to call forward ?
Why without forwarding tmp's correct c'tor won't be called even if T's type was deduced.
My second question is about reference collapsing rules:
A& && becomes A&
A&& && becomes A&&
so according this rules and taking in account universal reference why std::forward signature can't be as follows:
template<class T>
T&& forward(T&& arg){
return static_cast<T&&>(arg);
}
According to the rules from above if T's type is rvalue reference it will collapse to rvalue reference , if T's type is lvalue reference it will collapse to lvalue reference.
So why std::forward have two different signatures one for lvalue reference and one for rvalue reference am I missing something ?
My question is if universal reference type can be deduced why do I still need to call forward ?
Because as soon as you give a name to the parameter param it is an lvalue, even if the function was called with an rvalue, so it wouldn't be forwarded as an rvalue unless you use forward<T>
Why without forwarding tmp's correct c'tor won't be called even if T's type was deduced.
Because param is an lvalue. To restore the value category of the argument passed to foo you need to cast it back to string& or string&& which means you need to know the type T was deduced as, and use forward to do the cast.
So why std::forward have two different signatures one for lvalue reference and one for rvalue reference am I missing something ?
It was changed by http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3143.html
There is lots of background info in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3143.html and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2951.html
The problem with your suggested version is that if you say forward<string> then the parameter T is not deduced so doesn't function as a forwarding reference, which means that T&& can't bind to an lvalue, and it needs to be able to bind to an lvalue in order for forward<string>(param) to work, because param is an lvalue there.
As we have known, in most common cases, T&& means "this is a temporary object". However, if one wants to return a temporary object from a function, he/she can declare the function as follows:
template<class T>
T f()
{
T t;
......
return t;
}
or (Note: Not Correct)
template<class T>
T&& f()
{
T t;
......
return t;
}
But I think the latter is overdid, because the former is enough and backward compatible.
Yet, I also find the std::forward()'s return type is declared as T&&, so I'm sure my understanding about this is incomplete.
My real question is: When and where should we declare the return type of a function as T&&?
In your example, the T&& is wrong, it's a dangling reference.
But std::forward doesn't return an rvalue reference to a local variable in its own definition, it returns an rvalue reference to its by-rvalue-reference argument (or an lvalue reference to a by-lvalue-reference argument).
You should return an rvalue reference only if you want the caller of your function to be able to move from whatever that reference refers to.
Normally that will only be if the purpose of the function is to provide move access to some significant object (perhaps which already exists). So that includes std::move (which allows you to move from an lvalue), and similarly you might write an accessor function specifically designed for users to move from a data member of some object, or an element of some container. If the object itself isn't significant, only the value, then you can return by value.
As grizzly says, sometimes due to reference collapsing you can take advantage of tricks which mean that you type T&& in your code, but when T is already an lvalue-reference type T&& is the same lvalue reference type. std::forward uses that trick. That is to say, because of reference collapsing T&& doesn't mean "rvalue reference to T", it means "T if T is a reference type, otherwise rvalue reference to T".
T&& doesn't necessarily mean that the result is an rvalue. When used with a template parameter && denotes a universal reference, which can either be an rvalue or an lvalue reference. More specifically: If T is an lvalue reference type foo&, T&& is actually an lvalue refere to foo, otherwise it denotes an rvalue reference.
This can be used to write functions, which take any kind of argument:
template<typename T> void foo(T&&);
bar a;
const bar b;
foo(a);//T and T&& will both be bar&
foo(b);//T and T&& will both be const bar&
foo(bar());//T will be bar, T&& will be bar&&
With this in mind std::forward is called with a T& and casts it to T&&, where T is explicitly stated. So if the original function parameter was an lvalue reference, it will return one, otherwise it will return an rvalue reference, enabling perfect forwarding.
As for when to use it as a return type: Rarely, though it might be useful to avoid copies when arguments are passed through, for example:
template<typename T> T&& foo(T&& bar) {/*some ops*/ return std::forward<T>(bar);}
The answer depends on whether your function is a template function or not. In your question it was, but let's first take a look at if it isn't:
Non-Template
T&& is not meaningless as a return type. The reason is that it, in fact, does not mean "temporary object". It means "rvalue reference". The difference is subtle, but can be highlighted with a class of functions for which this return type is relevant. Namely, what if we want to return a reference, to an rvalue, but the object it refers to is not a local object of our function?
T&& return_rvalue(/*some data*/)
{
T&& t = Get_a_reference();
// Do something fascinating.
return static_cast<T&&>(t);
}
One very special case of this pattern is the function std::move, which takes any reference and returns it a corresponding rvalue reference. Naturally, in real code, you should of course use std::move rather than performing the cast directly, since this more clearly shows your intention.
Template
If T is a template parameter, T&& is what Scott Meyers refers to as a Universal Reference. This means the type of T&& will be figured out using reference collapsing... In short, it means T&& is an rvalue if T i not a reference type, and an lvalue reference if it is..
I saw this here:
Move Constructor calling base-class Move Constructor
Could someone explain:
the difference between std::move and std::forward, preferably with some code examples?
How to think about it easily, and when to use which
std::move takes an object and allows you to treat it as a temporary (an rvalue). Although it isn't a semantic requirement, typically a function accepting a reference to an rvalue will invalidate it. When you see std::move, it indicates that the value of the object should not be used afterwards, but you can still assign a new value and continue using it.
std::forward has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it. This allows rvalue arguments to be passed on as rvalues, and lvalues to be passed on as lvalues, a scheme called "perfect forwarding."
To illustrate:
void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }
template< typename t >
/* "t &&" with "t" being template param is special, and adjusts "t" to be
(for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
std::cout << "via std::forward: ";
overloaded( std::forward< t >( arg ) );
std::cout << "via std::move: ";
overloaded( std::move( arg ) ); // conceptually this would invalidate arg
std::cout << "by simple passing: ";
overloaded( arg );
}
int main() {
std::cout << "initial caller passes rvalue:\n";
forwarding( 5 );
std::cout << "initial caller passes lvalue:\n";
int x = 5;
forwarding( x );
}
As Howard mentions, there are also similarities as both these functions simply cast to reference type. But outside these specific use cases (which cover 99.9% of the usefulness of rvalue reference casts), you should use static_cast directly and write a good explanation of what you're doing.
Both std::forward and std::move are nothing but casts.
X x;
std::move(x);
The above casts the lvalue expression x of type X to an rvalue expression of type X (an xvalue to be exact). move can also accept an rvalue:
std::move(make_X());
and in this case it is an identity function: takes an rvalue of type X and returns an rvalue of type X.
With std::forward you can select the destination to some extent:
X x;
std::forward<Y>(x);
Casts the lvalue expression x of type X to an expression of type Y. There are constraints on what Y can be.
Y can be an accessible Base of X, or a reference to a Base of X. Y can be X, or a reference to X. One can not cast away cv-qualifiers with forward, but one can add cv-qualifiers. Y can not be a type that is merely convertible from X, except via an accessible Base conversion.
If Y is an lvalue reference, the result will be an lvalue expression. If Y is not an lvalue reference, the result will be an rvalue (xvalue to be precise) expression.
forward can take an rvalue argument only if Y is not an lvalue reference. That is, you can not cast an rvalue to lvalue. This is for safety reasons as doing so commonly leads to dangling references. But casting an rvalue to rvalue is ok and allowed.
If you attempt to specify Y to something that is not allowed, the error will be caught at compile time, not run time.
std::forward is used to forward a parameter exactly the way it was passed to a function. Just like shown here:
When to use std::forward to forward arguments?
Using std::move offers an object as an rvalue, to possibly match a move constructor or a function accepting rvalues. It does that for std::move(x) even if x is not an rvalue by itself.
I think comparing two example implementations can provide a lot of insight on what they are for and how they differ.
Let's start with std::move.
std::move
Long story short: std::move is for turning anything into an rvalue(¹), for the purpose of making it look like a temporary (even if it isn't: std::move(non_temporary)), so that its resources can be stolen from it, i.e. moved from it (provided this is not prevented by a const attribute; yes, rvalues can be const, in which case you can't steal resources from them).
std::move(x) says Hi guys, be aware that who I'm giving this x to can use and break it apart as he likes, so you typically use it on rvalue references parameters, because you're sure they are bound to temporaries.
This is a C++14 implementation of std::move very similar to what Scott Meyers shows in Effective Modern C++ (in the book the return type std::remove_reference_t<T>&& is changed to decltype(auto), which deduces it from the return statement)
template<typename T>
std::remove_reference_t<T>&& move(T&& t) {
return static_cast<std::remove_reference_t<T>&&>(t);
}
From this we can observe the following about std::move:
it is a template function, so it works on any type T;
it takes its sole parameter via universal (or forwarding) reference T&&, so it can operate on both lvalues and rvalues; T will correspondingly be deduced as an lvalue reference or as a non-reference type;
template type deduction is in place, so you don't have to specify the template argument via <…>, and, in practice, you should never specify it;
this also means that std::move is nothing more than a static_cast with the template argument automatically determined based on the non-template argument, whose type is deduced;
it returns an rvalue without making any copy, by using an rvalue reference type (instead of a non-reference type) for the return type; it does so by stripping any reference-ness from T, via std::remove_reference_t, and then adding &&.
Trivia
Do you know that, beside the std::move from <utility> that we are talking about, there's another one? Yeah, it's std::move from <algorithm>, which does a mostly unrelated thing: it's a version of std::copy which, instead of copying values from one container to another, it moves them, using std::move from <utility>; so it is a std::move which uses the other std::move!
std::forward
Long story short: std::forward is for forwarding an argument from inside a function to another function while telling the latter function whether the former was called with a temporary.
std::forward<X>(x) says one of two things:
(if x is bound to an rvalue, i.e. a temporary) Hi Mr Function, I've received this parcel from another function who doesn't need it after you work with it, so please feel free to do whatever you like with it;
(if x is bound to an lvalue, i.e. a non-temporary) Hi Mr Function, I've received this parcel from another function who does need it after you work with it, so please don't break it.
So you typically use it on forwarding/universal references, because they can bind to both temporaries and non temporaries.
In other words, std::forward is for being able to turn this code
template<typename T>
void wrapper(T&& /* univ. ref.: it binds to lvalues as well as rvalues (temporaries)*/ t) {
// here `t` is an lvalue, so it doesn't know whether it is bound to a temporary;
// `T` encodes this missing info, but sadly we're not making `some_func` aware of it,
// therefore `some_func` will not be able to steal resources from `t` if `t`
// is bound to a temporary, because it has to leave lvalues intact
some_func(t);
}
into this
template<typename T>
void wrapper(T&& /* univ. ref.: it binds to lvalues as well as rvalues (temporaries)*/ t) {
// here `t` is an lvalue, so it doesn't know whether it is bound to a temporary;
// `T` encodes this missing info, and we do use it:
// `t` bound to lvalue => `T` is lvalue ref => `std::forward` forwards `t` as lvalue
// `t` bound to rvalue => `T` is non-ref => `std::forward` turns `t` into rvalue
some_func(std::forward<T>(t));
}
This is the C++14 implementation of std::forward from the same book:
template<typename T>
T&& forward(std::remove_reference_t<T>& t) {
return static_cast<T&&>(t);
}
From this we can observe the following about std::forward:
it is a template function, so it works on any type T;
it takes its sole parameter via lvalue reference to a reference-less T; note that, because of Reference collapsing (see here), std::remove_reference_t<T>& resolves exactly to the same thing as T& would resolve to; however...
... the reason why std::remove_reference_t<T>& is used instead of T& is exactly to put T in a non-deduced context (see here), thus disabling template type deduction, so that you are forced to specify the template argument via <…>
this also means that std::forward is nothing more than a static_cast with the template argument automatically determined (via reference collapsing) based on the template argument that you must pass to std::forward;
it returns an rvalue or an lvalue without making any copy, by using an rvalue reference or lvalue reference type (instead of a non-reference type) for the return type; it does so by relying on reference collapsing applied to T&&, where T is the one that you passed as template argument to std::forward: if that T is a non-reference, then T&& is an rvalue reference, whereas if T is an lvalue reference, then T&& is an lvalue reference too.
¹ Scott Meyers in Effective Modern C++ says precisely the following:
std::move unconditionally casts its argument to an rvalue