Lance LI
Lance LI

Reputation: 113

Why compiler doesn't choose rvalue-reference version of the `forward`?

I wrote a similar implementation of std::forward hoping to find out in which case the compiler will pick which version. The question is it seems never pick the rvalue-reference version.

#include <type_traits>
#include <iostream>
#include <string>
#include <utility>

using std::string;
using std::cout;
using std::endl;
using std::remove_reference;
using std::move;

namespace explicit_return {
template <typename type> type&& forward(typename remove_reference<type>::type&  value) { cout << "cp-"; return static_cast<type&&>(value); }
template <typename type> type&& forward(typename remove_reference<type>::type&& value) { cout << "mv-"; return static_cast<type&&>(value); }
}

void print(string const & value) { cout << "c:" << value << endl; }
void print(string &  value)      { cout << "l:" << value << endl; }
void print(string && value)      { cout << "r:" << value << endl; }

template <typename type> void explicit_print(type && value) {          print(explicit_return::forward<type>(value)); }
template <typename type> void indirect_print(type && value) { explicit_print(explicit_return::forward<type>(value)); }

int main()
{
    string a("perfect");
    indirect_print(a);
    indirect_print(move(a));
    indirect_print("forward");
}

Let's see the output

cp-cp-l:perfect
cp-cp-r:perfect
cp-cp-r:forward

Upvotes: 2

Views: 73

Answers (2)

Peter  Taran
Peter Taran

Reputation: 150

Despite of you have declared indirect_print parameter as type &&, its value class is not an rvalue, but lvalue. Any named object is lvalue.

template <typename type>
void indirect_print(type && value) {
  explicit_print(explicit_return::forward<type>(value)); // `value` is lvalue here
}

That's why you're always call a type& version of your forward.

Remove explicit_print and indirect_print and rewrite main as:

int main()
{
    string a("perfect");

    print(explicit_return::forward<std::string>(a));
    print(explicit_return::forward<std::string>(move(a)));
    print(explicit_return::forward<std::string>("forward"));
}

and you will see the difference:

cp-r:perfect
mv-r:perfect
mv-r:forward

Upvotes: 0

Jarod42
Jarod42

Reputation: 217085

Argument you pass to forward<type> is a variable, so a l-value.

you might have r-value overload selected with extra std::move or extra forward when it was a r-value for example:

template <typename type> void print_forward2(type&& value)
{
     print(explicit_return::forward<type>(explicit_return::forward<type>(value)));
}

Demo

In practice, I can imagine that storing arguments in tuple and reapplying them (once), something along:

print(std::forward<Ts>(std::get<Ts>(std::move(my_tuple)))...);

Upvotes: 1

Related Questions