1

The forwarding reference is defined as follows: "Forwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward." (https://en.cppreference.com/w/cpp/language/reference)

By "preserve the value category of a function argument", I understand that the name of an argument variable whose type is a forwarding reference, has the same value category as in the function's invocation. For example:

template<typename T>
void wrapper(T&& forwarded_argument) {
    some_function(forwarded_argument);
}

wrapper(5);
wrapper(my_int);

Here, the expression 5 in the first invocation is a prvalue, so as I understand it, the expression forwarded_argument (just the name of the variable) within some_function(forwarded_argument);, should also be a prvalue. In the second invocation, the name my_int is an lvalue, so I would expect the expression forwarded_argument within some_function(forwarded_argument); to be an lvalue. This would make writing this line as std::forward<T>(forwarded_argument) unnecessary, since in both cases, the correct overload of some_function would be called anyway, based on what I pass to wrapper.

I also think I don't fully understand what std::forward actually does. Here's a simplified implementation I found (The implementation of std::forward):

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

template <class 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);
}

As cppreference states (https://en.cppreference.com/w/cpp/language/value_category), a cast expression to an rvalue reference to an object type, such as static_cast<char&&>(x), is an xvalue. I don't see any mention of the value category of x, so I assume the value category of the whole cast expression is the same regardless. And the value category of a function call expression, whose return type is an rvalue reference to an object, is an xvalue. From this, it appears that no matter what I pass to std::forward, the value category of a call to std::forward will always end up being an xvalue.

Where is the mistake here? The conclusions in both parts of the question (about forwarding references and about std::forward) seem incorrect, judging by the way forwarding is usually done.

1 Answers1

1

Where is the mistake here?

It's here

Here, the expression 5 in the first invocation is a prvalue, so as I understand it, the expression forwarded_argument (just the name of the variable) within some_function(forwarded_argument);, should also be a prvalue.

An id-expression (a plain identifier, to be specific) that refers to a function parameter is an lvalue. We need to cast it in order to get the value category of the argument to the function. And std::forward provides the correct cast based on the encoded value category in the deduced template argument.

A reference variable was bound to the argument (regardless of value category). Thus it now as a name, and the rule of thumb to go by is that things with a name are lvalues1.


1 - Just a rule of thumb. It's the general behavior for the most part. And is enough to get a feel for how forwarding works.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458