How to write a forward function template?

I want understand more how std::forward works and how Explicit Template Arguments & Template Argument Deduction work along with Forwarding references, I’ve tried this example:

template <typename T>
T&& fwd_(typename std::remove_reference<T>::type& arg){
    std::cout << "fwd_(type&)n";
    return static_cast<T&&>(arg);
}

template <typename T>
T&& fwd_(typename std::remove_reference<T>::type&& arg){
    std::cout << "fwd_(type&&)n";
    ++arg;
    return static_cast<T&&>(arg);
}

int main(){

    int&& l = fwd_<int>(5);
    int&& m = fwd_<int&&>(7);
    int& j = fwd_<int&>(m);
    int& k = fwd_<int&>(7); // UB?
    std::cout << k << std::endl;

  std::cout << 'n';
}
  • I don’t know what happened to k? and how come I can pass an r-value as an l-value? (fwd_<int&>(7))?

I am sure that that l-value reference is bound to the function template parameter; not to the argument passed.

I am also sure that that expression yields undefined behavior because that parameter is destroyed when fwd_ returns.

  • I’ve tried that and changed the optimization level so I got different values because of UB.

  • Are my thoughts correct? And if so how could I write a clean and correct version that mimicks the library one std::forward? Thank you!

Answer

I don’t know what happened to k? and how come I can pass an r-value as an l-value? (fwd_<int&>(7))?

Yes this is dangerous, since T is int& and return value T&&, as a result of reference collapsing we get int& &&int&, i.e. you end up returning a reference to a temporary, which is not life-extended, thus causing UB after the temporary is destroyed at the end of the full-expression (the ;).

For this reason the C++ standard explicitly excludes this usage from std::forward:

template <class T> constexpr T&& forward(remove_reference_t<T>& t) noexcept;
template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
. . .

3 Remarks: If the second form is instantiated with an lvalue reference type, the program is ill-formed.

And libstdc++ for example has an assertion to fail early in this case:

  template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
    {
      static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
            " substituting _Tp is an lvalue reference type");
      return static_cast<_Tp&&>(__t);
    }

Leave a Reply

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