Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need second std::forward specialization?

As far as I can understand from this explanation of how std::forward works http://thbecker.net/articles/rvalue_references/section_08.html we can dispense with only one version of std::forward:

template<class S>
S&& forward(typename remove_reference<S>::type& a) noexcept

But actually, we have (http://en.cppreference.com/w/cpp/utility/forward) second version:

template< class T >
T&& forward( typename std::remove_reference<T>::type&& t );

which differs from previous one only in how t is defined (with &&)

So, why do we need it? What will break if remove it?

like image 536
Void Avatar asked Aug 05 '14 08:08

Void


1 Answers

The arguments to the std::forward overloads (using the std::remove_reference) removes any reference collapsing and argument deduction that would have otherwise taken place and forces the lvalue and rvalue references to bind to the correct overload. This is also done without adding or removing any possible const that were part of (or not part of) the original argument T (i.e. we don't use a const T& because it essentially adds a const to the rvalue that T&& does not, yet they can both bind to the same rvalue).

A key error check is done in the rvalue reference overload; it is a sanity check to ensure that std::forward is not called to forward to an lvalue reference when an rvalue is provided; basically make sure that code like std::forward<int&>(42); fails to compile. std::forward is intended to be used in the formulaic std::forward<T> where T is from the deduced context T&& as noted in the reference. The overload effects the same results as the conditional return with the required error checking.

The implementation(s) of std::forward is geared towards the "conditional cast" that Scott Meyers talks about in his Going Native 2013 talk. Scott Meyers gives the following pseudo code in his presentation to explain the workings of std::forward (at approximately the 20 minute mark);

template <typename T>
T&& forward(T&& param) { // T&& here is formulated to disallow type deduction
  if (is_lvalue_reference<T>::value) {
    return param; // return type T&& collapses to T& in this case
  }
  else {
    return move(param); // return type is T&&
  }
}

std::forward is implemented to return an lvalue reference if the type of its argument is an lvalue, and an rvalue reference (equivalent to std::move) if the type of its argument type is an rvalue.


TL;DR Why is it needed? Basically; it protects against incorrect usage of std::forward (e.g. dangling references, references to temporaries no longer available, modify/modified literals etc.).

like image 162
Niall Avatar answered Oct 03 '22 14:10

Niall