Search code examples
c++c++11forward-declaration

How do two overloaded std::forward work?


I have found in std library the following implementation of std::forward:

// TEMPLATE FUNCTION forward
template<class _Ty> inline
constexpr _Ty&& forward(
    typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT
{   // forward an lvalue as either an lvalue or an rvalue
return (static_cast<_Ty&&>(_Arg));
}

template<class _Ty> inline
constexpr _Ty&& forward(
    typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{   // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}

First function works obviously, but for second I cannot find usefull example. If I try to do something like this:

template<class _Ty> inline
constexpr _Ty&& my_forward(
   typename std::remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{   // forward an rvalue as an rvalue
   static_assert(!std::is_lvalue_reference<_Ty>::value, "bad forward call");
   return (static_cast<_Ty&&>(_Arg));
}

template<typename T>
T getRValue()
{
    return std::remove_reference<T>::type{};
}

template<typename T>
void setValue(T && i)
{
   my_forward<T>(getRValue<T>());
}

int main()
{
    int i = 1;
    setValue(i);
}

to check when second function is called, I have got the error:

Error   C2664   '_Ty my_forward<T>(int &&) noexcept': cannot convert argument 1 from 'int' to 'int &&'

Does anybody know in which case second overloaded function (in my terms my_forward) is called ? Or may be some good example for it ? By the way my compiler is MSVC 2015

Thanks, all for help !!


Solution

  • I have found cases in which this function is called:

    my_forward<int&>(8); // This calls forward(typename remove_reference<_Ty>::type&& _Arg)
    But it won't compile 'cause we try convert rvalue to lvalue
    
    my_forward<int&&>(8); // This calls forward(typename remove_reference<_Ty>::type&& _Arg)
    But it compiles successfully
    

    If it may be useful for somebody, the purpose of std::forward is to forward operation to another class or function.

    Consider the following example:

    template<typename T>
    void precompute(T & t)
    {
       std::cout << "lvalue reference";
    }
    
    template<typename T, 
             std::enable_if_t<!std::is_reference<T>::value, bool> = true>
    void precompute(T && t)
    {
       std::cout << "rvalue reference";
    }
    
    template<typename T>
    void calculate(T && t)
    {
       precompute(t); // Every time called precompute(T & t)
    }
    

    or like this:

    template<typename T>
    void precompute(T & t)
    {
       std::cout << "lvalue reference";
    }
    
    template<typename T, 
             std::enable_if_t<!std::is_reference<T>::value, bool> = true>
    void precompute(T && t)
    {
       std::cout << "rvalue reference";
    }
    
    template<typename T>
    void calculate(T && t)
    {
       precompute(std::move(t)); // Every time called precompute(T && t)
    }
    

    As you can see we have a problem !! Neither of two examples satisfied us.

    In first example every time will be called lvalue function and nothing can be done to call first with rvalue.

    In second example every time will be called rvalue function and nothing can be done to call first with lvalue.

    The solution is to forward making decision to the called function:

    template<typename T>
    void precompute(T & t)
    {
       std::cout << "lvalue reference";
    }
    
    template<typename T, 
             std::enable_if_t<!std::is_reference<T>::value, bool> = true>
    void precompute(T && t)
    {
       std::cout << "rvalue reference";
    }
    
    template<typename T>
    void calculate(T && t)
    {
       precompute(std::forward<T>(t)); // Will be called either precompute(T & t) or precompute(T && t) depends on type of t
    }
    

    In this case we have to be sure that will be called appropriate version. That is why we forward (means: "Please, check type of t and if it is rvalue -> call rvalue version of function, and if it is lvalue -> call lvalue version of function") this operation to called function.