How std forward( ) converts to r-value in c++?

Share the Article

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; }

Main Funda: std::forward( ) do not forward anything, but it casts its argument to R-value during compile time on the basis of reference collapsing rules

Related Topics:

 What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
What is reference collapsing?
How std::move() function works?
How delete keyword can be used to filter polymorphism
Rule of Three

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *