Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

like image 747
openyourmind Avatar asked May 29 '19 13:05

openyourmind


People also ask

What is the use of std :: forward?

The idiomatic use of std::forward is inside a templated function with an argument declared as a forwarding reference , where the argument is now lvalue , used to retrieve the original value category, that it was called with, and pass it on further down the call chain (perfect forwarding).

What is forwarding reference in C++?

When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.

Why does STD forward need a template parameter?

Because for std::forward to work as intended(, i.e. to faitfully pass the original type info), it is meant to be used INSIDE TEMPLATE CONTEXT, and it must use the deduced type param from the enclosing template context, instead of deducing the type param by itself(, since only the enclosing templates have the chance to ...

What is C++ perfect forwarding?

What is Perfect Forwarding. Perfect forwarding allows a template function that accepts a set of arguments to forward these arguments to another function whilst retaining the lvalue or rvalue nature of the original function arguments.

What is the overload of a forwarding reference?

When t is a forwarding reference (a function argument that is declared as an rvalue reference to a cv-unqualified function template parameter), this overload forwards the argument to another function with the value category it had when passed to the calling function.

What is an rvalue overload in JavaScript?

This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument. For example, if a wrapper does not just forward its argument, but calls a member function on the argument, and forwards its result:

Can't forward an rvalue as an lvalue?

For example, if a wrapper does not just forward its argument, but calls a member function on the argument, and forwards its result: Attempting to forward an rvalue as an lvalue, such as by instantiating the form (2) with lvalue reference type T, is a compile-time error.

What are Rvalue references in C++?

Rvalue references are a feature of C++ that was added with the C++11 standard. What makes rvalue references a bit difficult to grasp is that when you first look at them, it is not clear what their purpose is or what problems they solve. Therefore, I will not jump right in and explain what rvalue references are.


1 Answers

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.

like image 73
Howard Hinnant Avatar answered Oct 18 '22 05:10

Howard Hinnant