Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++11: subtlety of std::forward: Is identity really necessary?

I set up a test case to learn about perfect forwarding.

std::string inner(const std::string& str ) {
return "const std::string&";
}
std::string inner(std::string& str ) {
    return "std::string&";
}
std::string inner(const std::string&& str ) {
    return "const std::string&&";
}
std::string inner(std::string&& str ) {
    return "std::string&&";
}

template <typename T> void outer(T&& t) {
  std::cout << "t: " << t << std::endl;
  std::cout << "perfect forward: " << inner(std::forward<T>(t)) << std::endl;
  std::cout << std::endl;
}

void PerfectForwarding()
{
     outer("literal");
     outer(lvalue);
     outer(constlvalue);
     outer(rvalue());
     outer(constrvalue());
}

std::forward works as expected. The interesting behavior emerges when I implement my own forward function without identity:

template <typename T> T&& MyForward(T& t)
{
   return ((T&&)t);
}

Replacing std::forward with MyForward in outer gives the exact same result! The behavior begs the question why identity is used?

Compiler VS2010

Update 1: In reference to preventing type deduction

AFAIK, the special type deduction rule is only activated on T&&. Note the definition of forward, forward(typename identity<T>::type& t). The argument type has only one &. In fact, after I changed MyForward to use identity, and leave out the (T&&) casting, the example does not compile. On the surface, the casting from lvalue to rvalue seems to make the forward work.

Update 2: tested on ideone.com with GCC 4.5, same behavior.

like image 366
Candy Chiu Avatar asked Apr 23 '12 18:04

Candy Chiu


People also ask

Why do we need std :: forward?

std::forward helps to implement perfect forwarding. This mechanism implies that objects passed to the function as lvalue expressions should be copied, and objects passed to the function as rvalue expressions should be moved. If you assign an rvalue reference to some ref variable, then ref is a named entity.

What is the difference between std :: forward and std :: move?

std::move takes an object and casts it as an rvalue reference, which indicates that resources can be "stolen" from this object. std::forward has a single use-case: to cast a templated function parameter of type forwarding reference ( T&& ) to the value category ( lvalue or rvalue ) the caller used to pass it.

What is perfect forwarding C++?

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.


1 Answers

remove_reference<T> (identity was in an old version of the draft, but was changed to remove_reference) is used to prevent type deduction: std::forward only works with an explicit type parameter. Otherwise the following would compile:

std::forward(t)

... but it would not do the right thing.

Regarding the issue with lvalues/rvalues, please do notice that there are two overloads of std::forward: one for lvalues, another for rvalues.

In fact, the MyForward implementation given is more like std::move: it turns lvalues into rvalues (the difference is that move accepts rvalues as well).

like image 77
R. Martinho Fernandes Avatar answered Sep 27 '22 21:09

R. Martinho Fernandes