confused with the fowarding reference

Viewed 80

In the c++ std type_traits file below the first overloaded function, the comment says:

forward an lvalue as either an lvalue or an rvalue

However the return value is just an rvalue reference, I wonder how it could be either an lavlue or an rvalue? Does it mean the returned value is a universal reference? If so what decides how to deduce it to an lvalue reference or an rvalue reference?

Also the second overload returns exactly the same thing, why does it say in the comment only forwarding as a rvalue reference without lvalue reference?

template <class _Ty>
    _NODISCARD constexpr _Ty&& forward(
        remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
        return static_cast<_Ty&&>(_Arg);
    }
    
    template <class _Ty>
    _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue
        static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
        return static_cast<_Ty&&>(_Arg);
    }
1 Answers

If _Ty is either a rvalue reference or a non-reference, then _Ty&& is (by reference collapsing rules) a rvalue reference. Hence the function call expression will be a xvalue (a kind of rvalue).

If _Ty is however an lvalue reference to type T, i.e. _Ty = T&, then the reference collapsing rules imply that also _Ty&& = T & && = T&. So then the function call will be an lvalue expression.

This is an implementation of std::forward. The template argument for _Ty is not intended to (and can't be) deduced. Instead it must be given explicitly.

Normally std::forward should only be used in the form std::forward<U>(u); where u is a forwarding reference parameter of the form U&& u in a function template with U a template parameter.

Under these conditions, if a rvalue was passed for u, U will be deduced to a non-reference and if an lvalue was passed U will be deduced to an lvalue reference.

Then std::forward<U>(u) will pass either a non-reference or an lvalue reference type as template argument for _Ty to std::forward accordingly and by the rule above, std::forward<U>(u) will have the same value category (rvalue or lvalue) as the argument to the forwarding reference u had. (However, it maps both prvalues and xvalues to xvalues.)


In the second overload the comment doesn't mention forwarding as lvalue, because the static_assert will trigger if the user tried to use it that way. It should not be allowed to call std::forward with a lvalue reference template argument while the argument is a rvalue. That would not match the intended usage I discussed above.

Related