I've read few papers about && and I'm just curious if having:
void fnc_1(int&& p)
{
//...
}
void fnc(int&& r)
{
fnc_1(r);//am I suppose to/should I? call it like so:fnc_1(std::forward(r))
}
or just passing 'r' is enough?
fnc_1(r) won't compile, because r is an lvalue, just like any other variable, regardless of type. Yes, that's right, named rvalue references are lvalues, not rvalues.
fnc_1(std::forward(r)) also won't compile, because std::forward is specifically designed not to infer its template argument.
To pass an rvalue, either of the following would work:
fnc_1(std::move(r))
fnc_1(std::forward<int&&>(r))
fnc_1(std::forward<int>(r))
Using std::move is the idiomatic way to cast an lvalue to an rvalue, so I would recommend using that.
The std::forward template is usually for dependent types. Please read this question carefully to see whether it applies here. This is a difficult subject to master, so feel free to update your question with relevant details about your exact problem (using rvalue references for integers isn't terribly exciting...).
I believe your question is about the understanding of the basic properties of rvalue references. The rule of thumb to remember is:
whatever has a name is a lvalue (const or not).
whatever has no name is a rvalue.
Types with && bind to rvalues.
If you have a function...
void foo(SomeClass&& x)
{
// ... then here x has type SomeClass& !
}
then inside the body, x is a name, and therefore a l value. It really has type SomeClass&. You must use std::move to turn a SomeClass& into SomeClass&&:
void bar(SomeClass&& x)
{
// Since `x` has a name here, it is a Lvalue.
// Therefore it has type SomeClass&, what the signature doesn't indicate.
// We thus have to explicitly turn it into a rvalue:
foo(std::move(x));
}
Related
This question already has answers here:
Closed 11 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Possible Duplicate:
Advantages of using forward
I know what it does and when to use it but I still can't wrap my head around how it works. Please be as detailed as possible and explain when std::forward would be incorrect if it was allowed to use template argument deduction.
Part of my confusion is this:
"If it has a name, it's an lvalue" - if that's the case why does std::forward behave differently when I pass thing&& x vs thing& x?
I think the explanation of std::forward as static_cast<T&&> is confusing. Our intuition for a cast is that it converts a type to some other type -- in this case it would be a conversion to an rvalue reference. It's not! So we are explaining one mysterious thing using another mysterious thing. This particular cast is defined by a table in Xeo's answer. But the question is: Why? So here's my understanding:
Suppose I want to pass you an std::vector<T> v that you're supposed to store in your data structure as data member _v. The naive (and safe) solution would be to always copy the vector into its final destination. So if you are doing this through an intermediary function (method), that function should be declared as taking a reference. (If you declare it as taking a vector by value, you'll be performing an additional totally unnecessary copy.)
void set(const std::vector<T> & v) { _v = v; }
This is all fine if you have an lvalue in your hand, but what about an rvalue? Suppose that the vector is the result of calling a function makeAndFillVector(). If you performed a direct assignment:
_v = makeAndFillVector();
the compiler would move the vector rather than copy it. But if you introduce an intermediary, set(), the information about the rvalue nature of your argument would be lost and a copy would be made.
set(makeAndFillVector()); // set will still make a copy
In order to avoid this copy, you need "perfect forwarding", which would result in optimal code every time. If you're given an lvalue, you want your function to treat it as an lvalue and make a copy. If you're given an rvalue, you want your function to treat it as an rvalue and move it.
Normally you would do it by overloading the function set() separately for lvalues and rvalues:
set(const std::vector<T> & lv) { _v = v; }
set(std::vector<T> && rv) { _v = std::move(rv); }
But now imagine that you're writing a template function that accepts T and calls set() with that T (don't worry about the fact that our set() is only defined for vectors). The trick is that you want this template to call the first version of set() when the template function is instantiated with an lvalue, and the second when it's initialized with an rvalue.
First of all, what should the signature of this function be? The answer is this:
template<class T>
void perfectSet(T && t);
Depending on how you call this template function, the type T will be somewhat magically deduced differently. If you call it with an lvalue:
std::vector<T> v;
perfectSet(v);
the vector v will be passed by reference. But if you call it with an rvalue:
perfectSet(makeAndFillVector());
the (anonymous) vector will be passed by rvalue reference. So the C++11 magic is purposefully set up in such a way as to preserve the rvalue nature of arguments if possible.
Now, inside perfectSet, you want to perfectly pass the argument to the correct overload of set(). This is where std::forward is necessary:
template<class T>
void perfectSet(T && t) {
set(std::forward<T>(t));
}
Without std::forward the compiler would have to assume that we want to pass t by reference. To convince yourself that this is true, compare this code:
void perfectSet(T && t) {
set(t);
set(t); // t still unchanged
}
to this:
void perfectSet(T && t) {
set(std::forward<T>(t));
set(t); // t is now empty
}
If you don't explicitly forward t, the compiler has to defensively assume that you might be accessing t again and chose the lvalue reference version of set. But if you forward t, the compiler will preserve the rvalue-ness of it and the rvalue reference version of set() will be called. This version moves the contents of t, which means that the original becomes empty.
This answer turned out much longer than what I initially assumed ;-)
First, let's take a look at what std::forward does according to the standard:
§20.2.3 [forward] p2
Returns: static_cast<T&&>(t)
(Where T is the explicitly specified template parameter and t is the passed argument.)
Now remember the reference collapsing rules:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
(Shamelessly stolen from this answer.)
And then let's take a look at a class that wants to employ perfect forwarding:
template<class T>
struct some_struct{
T _v;
template<class U>
some_struct(U&& v)
: _v(static_cast<U&&>(v)) {} // perfect forwarding here
// std::forward is just syntactic sugar for this
};
And now an example invocation:
int main(){
some_struct<int> s1(5);
// in ctor: '5' is rvalue (int&&), so 'U' is deduced as 'int', giving 'int&&'
// ctor after deduction: 'some_struct(int&& v)' ('U' == 'int')
// with rvalue reference 'v' bound to rvalue '5'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int&&>(v)'
// this just turns 'v' back into an rvalue
// (named rvalue references, 'v' in this case, are lvalues)
// huzzah, we forwarded an rvalue to the constructor of '_v'!
// attention, real magic happens here
int i = 5;
some_struct<int> s2(i);
// in ctor: 'i' is an lvalue ('int&'), so 'U' is deduced as 'int&', giving 'int& &&'
// applying the reference collapsing rules yields 'int&' (& + && -> &)
// ctor after deduction and collapsing: 'some_struct(int& v)' ('U' == 'int&')
// with lvalue reference 'v' bound to lvalue 'i'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int& &&>(v)'
// after collapsing rules: 'static_cast<int&>(v)'
// this is a no-op, 'v' is already 'int&'
// huzzah, we forwarded an lvalue to the constructor of '_v'!
}
I hope this step-by-step answer helps you and others understand just how std::forward works.
It works because when perfect forwarding is invoked, the type T is not the value type, it may also be a reference type.
For example:
template<typename T> void f(T&&);
int main() {
std::string s;
f(s); // T is std::string&
const std::string s2;
f(s2); // T is a const std::string&
}
As such, forward can simply look at the explicit type T to see what you really passed it. Of course, the exact implementation of doing this is non-trival, if I recall, but that's where the information is.
When you refer to a named rvalue reference, then that is indeed an lvalue. However, forward detects through the means above that it is actually an rvalue, and correctly returns an rvalue to be forwarded.
While I was studying rvalue reference, I found a strange answer from stackoverflow.
The questioner wants to avoid code duplication between a function that receive parameter as a lvalue reference, and another one is a function that receive a rvalue reference. Both functions do the same thing.
Here is the issue:-
void foo(X& x) { /*complex thing*/ } //#A
void foo(X&& x) { /*complex SAME thing*/ } //#B
Here is the proposed solution. It is modified a bit by me:-
void foo(X& x) { /*complex thing*/ } //#A
void foo(X&& x) { foo(x); } //#B
Question
Why doesn't my version lead to stack overflow exception?
In my version, why foo#B call foo#A, but not foo#B?
More specifically, which C++ rule enforces this behavior?
According to the rule of value categories, as a named parameter, x is an lvalue. Then foo#A will be invoked, because x could be bound to lvalue-reference, but not rvalue-reference.
Note that the fact that x is declared as an rvalue-reference has nothing to do with the value category of x.
lvalue
The following expressions are lvalue expressions:
the name of a variable or a function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
You can use std::move to make it an xvalue (rvalue), then foo#B will be selected (as you expected).
Although you have an answer already saying where the C++ standard specifies this, let me also answer why it specifies this.
The idea behind rvalue reference function parameters is that the functions may assume the referenced object, or at least its contents, will no longer be used after the function returns.
Now suppose you have
void f(const X&x); // doesn't modify x
void f(X&&x); // clears x's data
and you try
void g(X&&x) {
f(x);
std::cout << "we just called f(" << x << ")" << std::endl;
}
This wouldn't be useful if it called f(X&&), because x's contents would be gone before they could get printed.
g needs to explicitly tell f that it's okay to take over the object. f cannot assume that g won't need it again later.
It might work if the compiler could somehow figure out that x is no longer needed, when g's body no longer refers to x, but that's a very complicated problem to solve and unless the rules spell out exactly when it should and shouldn't be done, compilers won't implement it the same way, making it very likely that code that works on one compiler breaks on the next.
I always read that std::forward is only for use with template parameters. However, I was asking myself why. See the following example:
void ImageView::setImage(const Image& image){
_image = image;
}
void ImageView::setImage(Image&& image){
_image = std::move(image);
}
Those are two functions which basically do the same; one takes an l-value reference, the other an r-value reference. Now, I thought since std::forward is supposed to return an l-value reference if the argument is an l-value reference and an r-value reference if the argument is one, this code could be simplified to something like this:
void ImageView::setImage(Image&& image){
_image = std::forward(image);
}
Which is kind of similar to the example cplusplus.com mentions for std::forward (just without any template parameters). I'd just like to know, if this is correct or not, and if not why.
I was also asking myself what exactly would be the difference to
void ImageView::setImage(Image& image){
_image = std::forward(image);
}
You cannot use std::forward without explicitly specifying its template argument. It is intentionally used in a non-deduced context.
To understand this, you need to really understand how forwarding references (T&& for a deduced T) work internally, and not wave them away as "it's magic." So let's look at that.
template <class T>
void foo(T &&t)
{
bar(std::forward<T>(t));
}
Let's say we call foo like this:
foo(42);
42 is an rvalue of type int.
T is deduced to int.
The call to bar therefore uses int as the template argument for std::forward.
The return type of std::forward<U> is U && (in this case, that's int &&) so t is forwarded as an rvalue.
Now, let's call foo like this:
int i = 42;
foo(i);
i is an lvalue of type int.
Because of the special rule for perfect forwarding, when an lvalue of type V is used to deduce T in a parameter of type T &&, V & is used for deduction. Therefore, in our case, T is deduced to be int &.
Therefore, we specify int & as the template argument to std::forward. Its return type will therefore be "int & &&", which collapses to int &. That's an lvalue, so i is forwarded as an lvalue.
Summary
Why this works with templates is when you do std::forward<T>, T is sometimes a reference (when the original is an lvalue) and sometimes not (when the original is an rvalue). std::forward will therefore cast to an lvalue or rvalue reference as appropriate.
You cannot make this work in the non-template version precisely because you'll have only one type available. Not to mention the fact that setImage(Image&& image) would not accept lvalues at all—an lvalue cannot bind to rvalue references.
I recommend reading "Effective Modern C ++" by Scott Meyers, specifically:
Item 23: Understand std::move and std::forward.
Item 24: Distinguish universal references for rvalue references.
From a purely technical perspective, the answer is yes: std::forward
can do it all. std::move isn’t necessary. Of course, neither function
is really necessary, because we could write casts everywhere, but I
hope we agree that that would be, well, yucky. std::move’s attractions
are convenience, reduced likelihood of error, and greater clarity.
rvalue-reference
This function accepts rvalues and cannot accept lvalues.
void ImageView::setImage(Image&& image){
_image = std::forward(image); // error
_image = std::move(image); // conventional
_image = std::forward<Image>(image); // unconventional
}
Note first that std::move requires only a function argument, while std::forward requires both a function argument and a template type argument.
Universal references (forwarding references)
This function accepts all and does perfect forwarding.
template <typename T> void ImageView::setImage(T&& image){
_image = std::forward<T>(image);
}
You have to specify the template type in std::forward.
In this context Image&& image is always an r-value reference and std::forward<Image> will always move so you might as well use std::move.
Your function accepting an r-value reference cannot accept l-values so it is not equivalent to the first two functions.
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.
This question already has answers here:
Closed 11 years ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
Possible Duplicate:
Advantages of using forward
I know what it does and when to use it but I still can't wrap my head around how it works. Please be as detailed as possible and explain when std::forward would be incorrect if it was allowed to use template argument deduction.
Part of my confusion is this:
"If it has a name, it's an lvalue" - if that's the case why does std::forward behave differently when I pass thing&& x vs thing& x?
I think the explanation of std::forward as static_cast<T&&> is confusing. Our intuition for a cast is that it converts a type to some other type -- in this case it would be a conversion to an rvalue reference. It's not! So we are explaining one mysterious thing using another mysterious thing. This particular cast is defined by a table in Xeo's answer. But the question is: Why? So here's my understanding:
Suppose I want to pass you an std::vector<T> v that you're supposed to store in your data structure as data member _v. The naive (and safe) solution would be to always copy the vector into its final destination. So if you are doing this through an intermediary function (method), that function should be declared as taking a reference. (If you declare it as taking a vector by value, you'll be performing an additional totally unnecessary copy.)
void set(const std::vector<T> & v) { _v = v; }
This is all fine if you have an lvalue in your hand, but what about an rvalue? Suppose that the vector is the result of calling a function makeAndFillVector(). If you performed a direct assignment:
_v = makeAndFillVector();
the compiler would move the vector rather than copy it. But if you introduce an intermediary, set(), the information about the rvalue nature of your argument would be lost and a copy would be made.
set(makeAndFillVector()); // set will still make a copy
In order to avoid this copy, you need "perfect forwarding", which would result in optimal code every time. If you're given an lvalue, you want your function to treat it as an lvalue and make a copy. If you're given an rvalue, you want your function to treat it as an rvalue and move it.
Normally you would do it by overloading the function set() separately for lvalues and rvalues:
set(const std::vector<T> & lv) { _v = v; }
set(std::vector<T> && rv) { _v = std::move(rv); }
But now imagine that you're writing a template function that accepts T and calls set() with that T (don't worry about the fact that our set() is only defined for vectors). The trick is that you want this template to call the first version of set() when the template function is instantiated with an lvalue, and the second when it's initialized with an rvalue.
First of all, what should the signature of this function be? The answer is this:
template<class T>
void perfectSet(T && t);
Depending on how you call this template function, the type T will be somewhat magically deduced differently. If you call it with an lvalue:
std::vector<T> v;
perfectSet(v);
the vector v will be passed by reference. But if you call it with an rvalue:
perfectSet(makeAndFillVector());
the (anonymous) vector will be passed by rvalue reference. So the C++11 magic is purposefully set up in such a way as to preserve the rvalue nature of arguments if possible.
Now, inside perfectSet, you want to perfectly pass the argument to the correct overload of set(). This is where std::forward is necessary:
template<class T>
void perfectSet(T && t) {
set(std::forward<T>(t));
}
Without std::forward the compiler would have to assume that we want to pass t by reference. To convince yourself that this is true, compare this code:
void perfectSet(T && t) {
set(t);
set(t); // t still unchanged
}
to this:
void perfectSet(T && t) {
set(std::forward<T>(t));
set(t); // t is now empty
}
If you don't explicitly forward t, the compiler has to defensively assume that you might be accessing t again and chose the lvalue reference version of set. But if you forward t, the compiler will preserve the rvalue-ness of it and the rvalue reference version of set() will be called. This version moves the contents of t, which means that the original becomes empty.
This answer turned out much longer than what I initially assumed ;-)
First, let's take a look at what std::forward does according to the standard:
§20.2.3 [forward] p2
Returns: static_cast<T&&>(t)
(Where T is the explicitly specified template parameter and t is the passed argument.)
Now remember the reference collapsing rules:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
(Shamelessly stolen from this answer.)
And then let's take a look at a class that wants to employ perfect forwarding:
template<class T>
struct some_struct{
T _v;
template<class U>
some_struct(U&& v)
: _v(static_cast<U&&>(v)) {} // perfect forwarding here
// std::forward is just syntactic sugar for this
};
And now an example invocation:
int main(){
some_struct<int> s1(5);
// in ctor: '5' is rvalue (int&&), so 'U' is deduced as 'int', giving 'int&&'
// ctor after deduction: 'some_struct(int&& v)' ('U' == 'int')
// with rvalue reference 'v' bound to rvalue '5'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int&&>(v)'
// this just turns 'v' back into an rvalue
// (named rvalue references, 'v' in this case, are lvalues)
// huzzah, we forwarded an rvalue to the constructor of '_v'!
// attention, real magic happens here
int i = 5;
some_struct<int> s2(i);
// in ctor: 'i' is an lvalue ('int&'), so 'U' is deduced as 'int&', giving 'int& &&'
// applying the reference collapsing rules yields 'int&' (& + && -> &)
// ctor after deduction and collapsing: 'some_struct(int& v)' ('U' == 'int&')
// with lvalue reference 'v' bound to lvalue 'i'
// now we 'static_cast' 'v' to 'U&&', giving 'static_cast<int& &&>(v)'
// after collapsing rules: 'static_cast<int&>(v)'
// this is a no-op, 'v' is already 'int&'
// huzzah, we forwarded an lvalue to the constructor of '_v'!
}
I hope this step-by-step answer helps you and others understand just how std::forward works.
It works because when perfect forwarding is invoked, the type T is not the value type, it may also be a reference type.
For example:
template<typename T> void f(T&&);
int main() {
std::string s;
f(s); // T is std::string&
const std::string s2;
f(s2); // T is a const std::string&
}
As such, forward can simply look at the explicit type T to see what you really passed it. Of course, the exact implementation of doing this is non-trival, if I recall, but that's where the information is.
When you refer to a named rvalue reference, then that is indeed an lvalue. However, forward detects through the means above that it is actually an rvalue, and correctly returns an rvalue to be forwarded.