0

I'd really like to finally get to understand this. Let's say std::forward is defined like this:

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

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

We have a function:

template <typename T>
void I_forward_arguments(T&& arg)
{
     forward<T>(arg);
}

I_forward_arguments(5);

I pass 5, which is an rvalue, and T in I_forward_arguments() is int type. It then calls forward with explicit template argument of int, meaning the call is no longer a deduced context and there are no universal references, and the two choices of functions to call are:

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

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

which when we remove the reference we end up with one taking an int& and another taking an int&&. Since the argument in I_forward_arguments is ALWAYS an lvalue, I don't understand how it gets through to the function taking the int&& version of forward. Can someone please explain this? I've spent a long time trying to figure it out.

JaMiT
  • 14,422
  • 4
  • 15
  • 31
Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • In your example, `forward(int&)` is in fact called - which goes ahead and casts the lvalue to rvalue reference `int&&`. The result of this `forward` call is an rvalue, which is what really matters. Whether `forward` returns an lvalue or an rvalue doesn't depend on what its argument is, but on what its template parameter `T` is - whether it's a reference or a non-reference type. – Igor Tandetnik Sep 05 '21 at 23:54
  • IgorTandetnik Wait, why is 'arg' (the parameter name/identifier) an int& while T is an int? in void func(T&& arg); I pass it an rvalue, then T is 'int', and 'arg' is 'int' also, isn't it? T is 'int' and arg is 'int&'? – Zebrafish Sep 05 '21 at 23:59
  • There is a trick to debug type deductions I usually use (maybe there is even more straightforward way with latest standard): declare incomplete template class: `template struct S;` then try instantiate it: `S s;` or `S s;` and check the type in error messages in unclear scenarios. – dewaffled Sep 06 '21 at 00:04
  • As you yourself noted, `arg` is an lvalue of type `int`. Overload resolution then prefers a function taking `int&` over one taking `int&&`. But that makes no difference, as both overloads produce the same result anyhow - an `int&&` rvalue. – Igor Tandetnik Sep 06 '21 at 00:05
  • Does this answer your question? [Second overload of std::foward (example on cppreference.com)](https://stackoverflow.com/questions/68651800/second-overload-of-stdfoward-example-on-cppreference-com) – xskxzr Sep 06 '21 at 01:19

1 Answers1

0

int&& parameter here is lvalue (reference to rvalue t).

By the §3.10.1.4:

An rvalue is an xvalue, a temporary object or subobject thereof, or a value that is not associated with an object.

Since t is not a temporary object, and it is associated with an object, it is an lvalue.

Damir Tenishev
  • 1,275
  • 3
  • 14