I was looking at the source code of the std::move function. What it does is to remove the reference and add a right reference(&&).
/**
* #brief Convert a value to an rvalue.
* #param __t A thing of arbitrary type.
* #return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
So, I was wondering if I can make a right reference and pass it to the function? So, here it is:
void f1(int&&) { std::cout << 1; }
void f1(int&) { std::cout << 2; }
int main() {
int&& x = 1;
f1(static_cast<decltype(x)>(x));
f1(static_cast<int&&>(x));
f1(x);
}
And, output: 112
I figured out that x is type of int&& because first two f1 do the same thing. But, what about the third one? Isn't it an int&& ? Why do I have to static_cast it in order to use it like a right reference?
I know there is a clam in "c++ templates complete guide" which is:
The fact that move semantics is not automatically passed through is intentional
and important. If it weren’t, we would lose the value of a movable object the first time we use it in a function.
I am still curious why they select different function at compile time even they have the same type?
You're confusing types and value categories, they're different things.
As a named variable, x's value category is lvalue, (its type is int&&.) lvalues could be bound to lvalue-reference, but can't be bound to rvalue-reference. So given f1(x);, f1(int&) will be selected.
To get f1(int&&) to be called you need to convert it to rvalue. The 1st and 2nd one select f1(int&&) because explicit conversion make rvalue expressions (more precisely xvalue expressions); which could be bound to rvalue-reference. And std::move works in similar way too.
The following expressions are xvalue expressions:
a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);
...
a cast expression to rvalue reference to object type, such as static_cast<char&&>(x);
...
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.
I think there's something I'm not quite understanding about rvalue references. Why does the following fail to compile (VS2012) with the error 'foo' : cannot convert parameter 1 from 'int' to 'int &&'?
void foo(int &&) {}
void bar(int &&x) { foo(x); };
I would have assumed that the type int && would be preserved when passed from bar into foo. Why does it get transformed into int once inside the function body?
I know the answer is to use std::forward:
void bar(int &&x) { foo(std::forward<int>(x)); }
so maybe I just don't have a clear grasp on why. (Also, why not std::move?)
I always remember lvalue as a value that has a name or can be addressed. Since x has a name, it is passed as an lvalue. The purpose of reference to rvalue is to allow the function to completely clobber value in any way it sees fit. If we pass x by reference as in your example, then we have no way of knowing if is safe to do this:
void foo(int &&) {}
void bar(int &&x) {
foo(x);
x.DoSomething(); // what could x be?
};
Doing foo(std::move(x)); is explicitly telling the compiler that you are done with x and no longer need it. Without that move, bad things could happen to existing code. The std::move is a safeguard.
std::forward is used for perfect forwarding in templates.
Why does it get transformed into int once inside the function body?
It doesn't; it's still a reference to an rvalue.
When a name appears in an expression, it's an lvalue - even if it happens to be a reference to an rvalue. It can be converted into an rvalue if the expression requires that (i.e. if its value is needed); but it can't be bound to an rvalue reference.
So as you say, in order to bind it to another rvalue reference, you have to explicitly convert it to an unnamed rvalue. std::forward and std::move are convenient ways to do that.
Also, why not std::move?
Why not indeed? That would make more sense than std::forward, which is intended for templates that don't know whether the argument is a reference.
It's the "no name rule". Inside bar, x has a name ... x. So it's now an lvalue. Passing something to a function as an rvalue reference doesn't make it an rvalue inside the function.
If you don't see why it must be this way, ask yourself -- what is x after foo returns? (Remember, foo is free to move x.)
rvalue and lvalue are categories of expressions.
rvalue reference and lvalue reference are categories of references.
Inside a declaration, T x&& = <initializer expression>, the variable x has type T&&, and it can be bound to an expression (the ) which is an rvalue expression. Thus, T&& has been named rvalue reference type, because it refers to an rvalue expression.
Inside a declaration, T x& = <initializer expression>, the variable x has type T&, and it can be bound to an expression (the ) which is an lvalue expression (++). Thus, T& has been named lvalue reference type, because it can refer to an lvalue expression.
It is important then, in C++, to make a difference between the naming of an entity, that appears inside a declaration, and when this name appears inside an expression.
When a name appears inside an expression as in foo(x), the name x alone is an expression, called an id-expression. By definition, and id-expression is always an lvalue expression and an lvalue expressions can not be bound to an rvalue reference.
When talking about rvalue references it's important to distinguish between two key unrelated steps in the lifetime of a reference - binding and value semantics.
Binding here refers to the exact way a value is matched to the parameter type when calling a function.
For example, if you have the function overloads:
void foo(int a) {}
void foo(int&& a) {}
Then when calling foo(x), the act of selecting the proper overload involves binding the value x to the parameter of foo.
rvalue references are only about binding semantics.
Inside the bodies of both foo functions the variable a acts as a regular lvalue. That is, if we rewrite the second function like this:
void foo(int&& a) {
foo(a);
}
then intuitively this should result in a stack overflow. But it doesn't - rvalue references are all about binding and never about value semantics. Since a is a regular lvalue inside the function body, then the first overload foo(int) will be called at that point and no stack overflow occurs. A stack overflow would only occur if we explicitly change the value type of a, e.g. by using std::move:
void foo(int&& a) {
foo(std::move(a));
}
At this point a stack overflow will occur because of the changed value semantics.
This is in my opinion the most confusing feature of rvalue references - that the type works differently during and after binding. It's an rvalue reference when binding but it acts like an lvalue reference after that. In all respects a variable of type rvalue reference acts like a variable of type lvalue reference after binding is done.
The only difference between an lvalue and an rvalue reference comes when binding - if there is both an lvalue and rvalue overload available, then temporary objects (or rather xvalues - eXpiring values) will be preferentially bound to rvalue references:
void goo(const int& x) {}
void goo(int&& x) {}
goo(5); // this will call goo(int&&) because 5 is an xvalue
That's the only difference. Technically there is nothing stopping you from using rvalue references like lvalue references, other than convention:
void doit(int&& x) {
x = 123;
}
int a;
doit(std::move(a));
std::cout << a; // totally valid, prints 123, but please don't do that
And the keyword here is "convention". Since rvalue references preferentially bind to temporary objects, then it's reasonable to assume that you can gut the temporary object, i.e. move away all of its data away from it, because after the call it's not accessible in any way and is going to be destroyed anyway:
std::vector<std::string> strings;
string.push_back(std::string("abc"));
In the above snippet the temporary object std::string("abc") cannot be used in any way after the statement in which it appears, because it's not bound to any variable. Therefore push_back is allowed to move away its contents instead of copying it and therefore save an extra allocation and deallocation.
That is, unless you use std::move:
std::vector<std::string> strings;
std::string mystr("abc");
string.push_back(std::move(mystr));
Now the object mystr is still accessible after the call to push_back, but push_back doesn't know this - it's still assuming that it's allowed to gut the object, because it's passed in as an rvalue reference. This is why the behavior of std::move() is one of convention and also why std::move() by itself doesn't actually do anything - in particular it doesn't do any movement. It just marks its argument as "ready to get gutted".
The final point is: rvalue references are only useful when used in tandem with lvalue references. There is no case where an rvalue argument is useful by itself (exaggerating here).
Say you have a function accepting a string:
void foo(std::string);
If the function is going to simply inspect the string and not make a copy of it, then use const&:
void foo(const std::string&);
This always avoids a copy when calling the function.
If the function is going to modify or store a copy of the string, then use pass-by-value:
void foo(std::string s);
In this case you'll receive a copy if the caller passes an lvalue and temporary objects will be constructed in-place, avoiding a copy. Then use std::move(s) if you want to store the value of s, e.g. in a member variable. Note that this will work efficiently even if the caller passes an rvalue reference, that is foo(std::move(mystring)); because std::string provides a move constructor.
Using an rvalue here is a poor choice:
void foo(std::string&&)
because it places the burden of preparing the object on the caller. In particular if the caller wants to pass a copy of a string to this function, they have to do that explicitly;
std::string s;
foo(s); // XXX: doesn't compile
foo(std::string(s)); // have to create copy manually
And if you want to pass a mutable reference to a variable, just use a regular lvalue reference:
void foo(std::string&);
Using rvalue references in this case is technically possible, but semantically improper and totally confusing.
The only, only place where an rvalue reference makes sense is in a move constructor or move assignment operator. In any other situation pass-by-value or lvalue references are usually the right choice and avoid a lot of confusion.
Note: do not confuse rvalue references with forwarding references that look exactly the same but work totally differently, as in:
template <class T>
void foo(T&& t) {
}
In the above example t looks like a rvalue reference parameter, but is actually a forwarding reference (because of the template type), which is an entirely different can of worms.
Recently I've been trying to understand move semantics and came up with a question.
The question has already been discussed here.
I implemented the first variant and checked whether it returns l-value or r-value:
#include <iostream>
using namespace std;
template <typename T>
T&& my_forward(T&& x) {
return static_cast<T&&> (x);
}
int main() {
int a = 5;
&my_forward(a); // l-value
return 0;
}
So if I pass l-value, it returns l-value (compiles, because I can take an adress from l-value) and if I do that:
&my_forward(int(5)); // r-value with int&& type
My code doesn't compile, because my_forward returned r-value. In the question above they say that the difference between this implementation and standart one (with std::remove_reference and 2 different arguments with & and && respectively) is that my implementation returns l-value all the time, but as I've shown it returns both r-value and l-value.
So I wonder, why can't I implement std::forward like that? In what specific cases will it show difference between standart one? Also, why should I specify T as a template and can't let it define itself with argument type?
Try hsing it like std forward in a real context. Yours does not work;
void test(std::vector<int>&&){}
template<class T>
void foo(T&&t){
test(my_forward<T>(t));
}
foo( std::vector<int>{} );
The above does not compile. It does with std::forward.
Your forward does nothing useful other than block reference lifetime extension. Meanwhile, std::forward is a conditional std::move.
Everything with a name is an lvalue, but forward moves rvalue references with names.
Rvalue references with names are lvalues.
Unfortunately, taking the address is not a useful operation in your context, because it looks at the wrong kind of value category:
You can take the address of a glvalue, but not of a prvalue. A glvalue represents a "location" (i.e. where an object is), a prvalue represents "initialization" (i.e. what value an object has).
You can steal resources from an rvalue, but not from an lvalue. Lvalue references bind to lvalues, rvalue references bind to rvalues. The point of std::forward is to cast an argument to an rvalue when an rvalue was provided, and to an lvalue when an lvalue was provided.
When std::forward returns an rvalue, it actually returns an xvalue, and xvalues are both rvalues and glvalues:
lvalue f() for "T& f();", decltype(f()) is T&
/
glvalue
/ \
value xvalue f() for "T&& f();", decltype(f()) is T&&
\ /
rvalue
\
prvalue f() for "T f();", decltype(f()) is T
I got confused by the following perfect forwarding function, where the template parameter T can match rvalue or lvalue references:
template<typename T>
void foo(T&& t){
T::A; // intended error to inspect type
}
int main(){
std::vector<int> a;
std::vector<int> && b = std::move(a);
foo(b); // T is std::vector<int> &
foo(std::move(a)); // T is std::vector<int>
}
I dont understand why the template argument deduction of T in foo is so different in these two cases? Whats the fundamental difference and important what is t's type in function foo.
std::move(a) returns a rvalue reference and b is already a rvalue reference (but has a name).
Is that right that, b s type is a rvalue reference to std::vector<int>, but as far as my understanding goes, it has a name and is thus considered an lvalue in function main?
Can anyone shine some light into this :-)
There is a special type deduction rule when && is used with templates.
template <class T>
void func(T&& t) {
}
"When && appears in a type-deducing context, T&& acquires a special
meaning. When func is instantiated, T depends on whether the argument
passed to func is an lvalue or an rvalue. If it's an lvalue of type U,
T is deduced to U&. If it's an rvalue, T is deduced to U:"
func(4); // 4 is an rvalue: T deduced to int
double d = 3.14;
func(d); // d is an lvalue; T deduced to double&
float f() {...}
func(f()); // f() is an rvalue; T deduced to float
int bar(int i) {
func(i); // i is an lvalue; T deduced to int&
}
Also, reference collapsing rule is a good read.
Check this out for a really good explanation:
perfect forwarding
If you think about the signature of your function, the type of the parameter is T&&. In your second example, T is deduced to vector<int>, that means that the type of the parameter to your function is vector<int>&&. So you are still passing by (rvalue) reference.
In the other case, you deduce T to vector<int>&. So the type of the argument is vector<int> & &&... or it would be, but references to references are not allowed. Reference collapsing takes over, and any double reference involving an lvalue reference become an lvalue reference. So you are passing by lvalue reference.
As far as b goes, this is a well known gotcha of rvalue references. Essentially, b's type is rvalue reference, but b itself still has a value category of lvalue. Think of it this way: b itself is a variable, that must live on the stack somewhere, and have an address. So it's an lvalue. This is precisely way calling std::forward when forwarding arguments is necessary. If you didn't do it, then they would always be forwarded as lvalue arguments.
I really recommend this Scott Meyers article: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers. Read it carefully!
Is that right that, b s type is a rvalue reference to std::vector<int>, but as far as my understanding goes, it has a name and is thus considered an lvalue in function main?
Yes, that's exactly it. It makes more sense if you think about rvalue reference function parameters: the caller is specifying that the function can do whatever it wants with the objects it gets. So from inside the function body, in order to make sure the code really can do whatever it wants with it, the parameter should be treated as an lvalue. That same argument can also be made for other rvalue references, including the b in your example, albeit to a lesser extent.
The expressions a and b are both lvalues, and the expression std::move(a) is an rvalue.
The deduction for the parameter T makes use of special reference collapsing rules so that the type of t is either an lvalue or an rvalue reference as needed to bind to the function call argument.
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