Asking for a friend:
Why does std::forward in the following code cast the parameter c to an rvalue ?
template <typename T>
void f (T& c) {
using value_type = typename std::remove_reference_t<T>;
std::vector<value_type> v;
// debugger reveals: push_back( T&& value ) is called here
v.push_back(std::forward<T>(c));
}
Note that c is not a universal/forwarding reference here. I am aware of the fact that this function would most probably be of more use if it actually was, but curious all the same.
To understand this situation, you have to understand why forwarding references work. Given a definition like
template <typename T>
void foo(T&& t) {}
when you write something like
some_type some_object;
foo(some_object);
template deduction deduces T to be some_type&. Now the parameter t has the type some_type& &&. Since you can't have references to references, reference collapsing rules are applied and some_type& && is collapsed to some_type&.
If, instead, you write something like
some_type some_object;
foo(std::move(some_object));
template deduction deduces T to be some_type. Now the parameter t has the type some_type&&. That's a perfectly valid type, so no reference collapsing is done.
Now we get to std::forward. All std::forward<U> does is cast its parameter to U&&. If U is some_type, as in the second case above, the parameter is cast to some_type&&. It remains an rvalue-reference. If U is some_type&, as in the first case above, reference collapsing is performed again, and some_type& && becomes some_type&. So std::forward returns an lvalue-reference.
So the ultimate answer to your original question is that the return type of std::forward only depends on the type passed as std::forward's template parameter. Since T in your case will always be deduced as a non-reference type, std::forward will always return an rvalue-reference.
std::forward<T>(c) is equivalent to static_cast<T&&>(c).
If T&& is a forwarding reference then this allows an lvalue to be forwarded as an lvalue because T will be deduced as an lvalue reference type and T&& will be the same lvalue reference type by the reference-collapsing rules. In your situation, T&& is not a forwarding reference, so this doesn't work.
Well, the definition of std::forward<T>(x) is to cast x to type T&&. If you pass a non-reference as argument you’ll get an rvalue reference T&& back. Since your T cannot be a reference type (you cannot have a reference to a reference), it must be a non-reference type.
Related
In the c++ std type_traits file below the first overloaded function, the comment says:
forward an lvalue as either an lvalue or an rvalue
However the return value is just an rvalue reference, I wonder how it could be either an lavlue or an rvalue? Does it mean the returned value is a universal reference? If so what decides how to deduce it to an lvalue reference or an rvalue reference?
Also the second overload returns exactly the same thing, why does it say in the comment only forwarding as a rvalue reference without lvalue reference?
template <class _Ty>
_NODISCARD constexpr _Ty&& forward(
remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
return static_cast<_Ty&&>(_Arg);
}
template <class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
return static_cast<_Ty&&>(_Arg);
}
If _Ty is either a rvalue reference or a non-reference, then _Ty&& is (by reference collapsing rules) a rvalue reference. Hence the function call expression will be a xvalue (a kind of rvalue).
If _Ty is however an lvalue reference to type T, i.e. _Ty = T&, then the reference collapsing rules imply that also _Ty&& = T & && = T&. So then the function call will be an lvalue expression.
This is an implementation of std::forward. The template argument for _Ty is not intended to (and can't be) deduced. Instead it must be given explicitly.
Normally std::forward should only be used in the form std::forward<U>(u); where u is a forwarding reference parameter of the form U&& u in a function template with U a template parameter.
Under these conditions, if a rvalue was passed for u, U will be deduced to a non-reference and if an lvalue was passed U will be deduced to an lvalue reference.
Then std::forward<U>(u) will pass either a non-reference or an lvalue reference type as template argument for _Ty to std::forward accordingly and by the rule above, std::forward<U>(u) will have the same value category (rvalue or lvalue) as the argument to the forwarding reference u had. (However, it maps both prvalues and xvalues to xvalues.)
In the second overload the comment doesn't mention forwarding as lvalue, because the static_assert will trigger if the user tried to use it that way. It should not be allowed to call std::forward with a lvalue reference template argument while the argument is a rvalue. That would not match the intended usage I discussed above.
According to cppreference.com, move has signature
template< class T >
typename std::remove_reference<T>::type&& move( T&& t ) noexcept;
Why does it take a rvalue reference T&& t as its arugment?
Also when I tried the following code
void foo(int&& bar) {
cout << "baz" << endl;
}
int main(){
int a;
foo(a);
}
I got an error from the compiler "an rvalue reference cannot be bound to an lvalue"
What is going on? I'm so confused.
It's not an rvalue reference, but a forwarding reference; which could preserve the value category of the argument. That means std::move could take both lvalue and rvalue, and convert them to rvalue unconditionally.
Forwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward. Forwarding references are either:
1) function parameter of a function template declared as rvalue
reference to cv-unqualified type template parameter of that same
function template:
2) auto&& except when deduced from a brace-enclosed initializer list.
On the other hand, int&& is an rvalue reference; note the difference here, if a function template parameter has type T&& with template parameter T, i.e. a deduced type T, the parameter is a forwarding reference.
(constexpr and noexcept are left out, since they seem irrelevant for the purpose of understanding how std::forward behaves.)
Based on my understanding of Scott Meyers' "Effective Modern C++",
a sample implementation of std::move in C++14 is the following
template<typename T>
decltype(auto) move(T&& param) {
return static_cast<remove_reference_t<T>&&>(param);
}
Given the explanation of what a forwarding (or "universal") reference is, this implementation, I think, is pretty clear to me:
the parameter param is of type T&&, i.e. an rvalue reference or lvalue reference (to whichever the type of the argument is), depending on whether the argument is an rvalue or lvalue in the caller; in other words param can bind both to rvalues and lvalues (i.e. anything); this is intended, since move should cast anything to rvalue.
decltype(auto) is just the concise way to express the return type based on the actual return statement.
the returned object is the same object param, casted to an rvalue reference (&&) to whatever the type T is, once its deduced referenceness is stripped off (the deduction is done on T&&, not on ⋯<T>&&).
In short, my understanding of the use of forwarding/universal references in the implementation of move is the following:
the forwarding/universal reference T&& is used for the parameter since it is intended to bind to anything;
the return type is an rvalue reference, since move is intended to turn anything to rvalue.
It'd be nice to know if my understanding is right so far.
On the other hand, a sample implementation of std::forward in C++14 is the following
template<typename T>
T&& forward(remove_reference_t<T>& param) {
return static_cast<T&&>(param);
}
My understanding is the following:
T&&, the return type, must be a forwarding/universal reference, since we want forward to return either by rvalue reference or by lvalue reference, hence type deduction takes place on the return type here (unlike what happens for move, where type deduction takes place on the parameter side) is an rvalue reference to whichever template type argument is passed to forward;
since T encodes the lvalue/rvalue-ness of the actual argument which binds the callers' parameter that is passed as argument to forward, T itself can result to be actual_type& or actual_type, hence T&& can be either an lvalue reference or rvalue reference.
The type of param is an lvalue reference to whatever the type T is, once its deduced referenceness is stripped off. Actually in std::forward type deduction is disabled on purpose, requiring that the template type argument be passed explicitly.
My doubts are the following.
The two instances of forward (two for each type on which is it called, actually) only differ for the return type (rvalue reference when an rvalue is passed, lvalue reference when an lvalue is passed), since in both cases param is of type lvalue reference to non-const reference-less T. Isn't the return type something which does not count in overload resolution? (Maybe I've used "overload" improperly, here.)
Since the type of param is non-const lvalue reference to reference-less T, and since an lvalue reference must be to-const in order to bind to an rvalue, how can param bind to an rvalue?
As a side question:
can decltype(auto) be used for the return type, as it is done for move?
forward is essentially a machinery to conserve the value category in perfect forwarding.
Consider a simple function that attempts to call the f function transparently, respecting value category.
template <class T>
decltype(auto) g(T&& arg)
{
return f(arg);
}
Here, the problem is that the expression arg is always an lvalue regardless of whether arg is of rvalue reference type. This is where forward comes in handy:
template <class T>
decltype(auto) g(T&& arg)
{
return f(forward<T>(arg));
}
Consider a reference implementation of std::forward:
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept
{
return static_cast<T&&>(t);
}
template <class T>
constexpr T&& forward(remove_reference_t<T>&& t) noexcept
{
static_assert(!std::is_lvalue_reference_v<T>);
return static_cast<T&&>(t);
}
(You can use decltype(auto) here, because the deduced type will always be T&&.)
In all the following cases, the first overload is called because the expression arg denotes a variable and hence is an lvalue:
If g is called with a non-const lvalue, then T is deduced as a non-const lvalue reference type. T&& is the same as T, and forward<T>(arg) is a non-const lvalue expression. Therefore, f is called with a non-const lvalue expression.
If g is called with a const lvalue, then T is deduced as a const lvalue reference type. T&& is the same as T, and forward<T>(arg) is a const lvalue expression. Therefore, f is called with a const lvalue expression.
If g is called with an rvalue, then T is deduced as a non-reference type. T&& is an rvalue reference type, and forward<T>(arg) is an rvalue expression. Therefore, f is called with an rvalue expression.
In all cases, the value category is respected.
The second overload is not used in normal perfect forwarding. See What is the purpose of std::forward()'s rvalue reference overload? for its usage.
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..