Just like, std::move( ), std::forward( ) also casts its argument (to R-value), however, unlike std::move( ) which is an unconditional cast, std::forward( ) casts under certain conditions. The forward will do casting to R-value only when the argument is bound to an R-value.
Considering only a pure technical perspective, a forward( ) can do all and the move( ) isn’t necessary. However, use of move( ) function is more convenient. Basically, std::move( ) needs only single thing, i.e. function’s argument value. Whereas the forward( ) function needs 2 things – firstly, the function’s argument value and secondly a template type argument in < > (angular) brackets.
Move:
int && izvar = std::move(4);
Forward:
int &&ipvar = std::forward<int>(3);
Universal Reference Pointer :
Generally, std::forward is used with a universal reference parameter, which can bound either to L-value or R-value. The forward will do cast to R-value only when the universal reference parameter is bound to R-value.
#include <iostream> //main header
using namespace std; //for namespace
void funda(int&& avar) //R-value overload
{
cout << "R-Value overload called" << endl;
}
void funda(int& avar) //L-value overload
{
cout << "L-Value overload called" << endl;
}
template<typename T>
void call_funda(T&& parg) //Argument Universal Reference
{
funda(std::forward<T>(parg));
}
int main()
{
int mvar=5;
call_funda(mvar); // Calls L-value overload
call_funda(4); // Calls R-value overload
return 0;
}
When call_funda is called with L-value and R-value respectively, the corresponding overloads are called
Basic implementation of forward( ):
template <typename T>
T&& forward(typename std::remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}
The std::remove_reference template class is part of C++ library, however, this also can be easily implemented (see below).
template <class T>
struct remove_reference { typedef T type; };
template <class T>
struct remove_reference<const T> { typedef const T type; };
template <class T>
struct remove_reference<T&> { typedef T type; };
template <class T>
struct remove_reference<const T&> { typedef const T type; };
template <class T>
struct remove_reference<T&&> { typedef T type; };
template <class T>
struct remove_reference<const T&&> { typedef const T type; };
How it all works ?
The concept of forward works using reference collapsing rules. When the forward function is called with an argument which is L-value, then T will deduce to become L-value type.
Case1: if the argument is int&, then T will also become T&
Therefore, the code will become
int& && forward(typename std::remove_reference<int&>::type& param)
{
return static_cast<int& &&>(param);
}
The reference collapsing rules will make “&” of “&&” to become “&”, hence the forward( ) code will become as shown below:
int& forward(int& param)
{
return static_cast<int&>(param);
}
Clearly, the forward casts the param to L-value type.
Case2: if the argument is int&&, then T will also become T&&
Therefore, the code will become
int&& && forward(typename std::remove_reference<int&&>::type& param)
{
return static_cast<int&& &&>(param);
}
The reference collapsing rules will make “&&” of “&&” to become “&&”, hence the forward( ) code will become as shown below:
int&& forward(int& param)
{
return static_cast<int&&>(param);
}
Clearly, the forward casts the param to R-value type.
Demonstration of our basic implementation of forward( )
#include <iostream> //main header
using namespace std;//for namespace
void funda(int&& avar)
{
cout << "R-Value overload called" << endl;
}
void funda(int& avar)
{
cout << "L-Value overload called" << endl;
}
template <typename T>
T&& my_forward(typename std::remove_reference<T>::type& param)
{
return static_cast<T&&>(param);
}
template<typename T>
void call_funda(T&& parg)
{
funda(my_forward<T>(parg));
}
int main()
{
int mvar=5;
call_funda(mvar); // Calls L-value overload
call_funda(4); // Calls R-value overload
return 0;
}