c++c++11c++14c++17perfect-forwarding

What is the purpose of std::forward()'s rvalue reference overload?


I'm experimenting with Perfect Forwarding and I found that std::forward() needs two overloads:

Overload nr. 1:

template <typename T>
inline T&& forward(typename 
std::remove_reference<T>::type& t) noexcept
{
    return static_cast<T&&>(t);
}

Overload nr.2:

template <typename T>
inline T&& forward(typename 
std::remove_reference<T>::type&& t) noexcept
{
    static_assert(!std::is_lvalue_reference<T>::value,
              "Can not forward an rvalue as an lvalue.");
    return static_cast<T&&>(t);
}

Now a typical scenario for Perfect Forwarding is something like

template <typename T>
void wrapper(T&& e)
{
    wrapped(forward<T>(e));
}

Of course you know that when wrapper() is instantiated, T depends on whether the argument passed to it is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to U.

In any case - in the scope of wrapper() - e is an lvalue, therefore it always uses the first overload of std::forward().

Now my question:

What is a valid scenario in which the 2nd overload is used (and is needed)?


Solution

  • The design rationale for forward is discussed in great detail in N2951.

    This document lays out 6 use cases:

    A. Should forward an lvalue as an lvalue. All implementations pass this test. But this is not the classic perfect forwarding pattern. The purpose of this test is to show that implementation 2 fails in its stated goal of preventing all use cases except perfect forwarding.

    B. Should forward an rvalue as an rvalue. Like use case A, this is an identity transformation and this presents a motivating example where the identity transformation is needed.

    C. Should not forward an rvalue as an lvalue. This use case demonstrates a dangerous situation of accidentally creating a dangling reference.

    D. Should forward less cv-qualified expressions to more cv-qualified expressions. A motivating use case involving the addition of const during the forward.

    E. Should forward expressions of derived type to an accessible, unambiguous base type. A motivating use case involving forwarding a derived type to a base type.

    F. Should not forward arbitrary type conversions. This use case demonstrates how arbitrary conversions within a forward lead to dangling reference run time errors.

    The second overload enables cases B and C.

    The paper goes on to provide examples of each use case, which are too lengthy to be repeated here.

    Update

    I've just run the "solution" of just the first overload through these 6 use cases, and this exercise shows that the second overload also enables use case F: Should not forward arbitrary type conversions.