In h(X &&)
, the type of t
is an r-value reference to X
, but named variables are always treated as l-values. So, even though t
is an X &&
, t
can not bind directly to an X &&
parameter, but only an X &
parameter. This is for safety, since a named variable can (and often will) be used over and over again. You don't want the first usage of a variable to pilfer it, even if it did originally bind to an r-value. Subsequent uses would see the pilfered value, which will very easily lead to broken logic in code.
If you know a variable is an r-value (or more to the point, if you know you're done with it, whether it's an l-value or an r-value), the way to pass it on as an r-value is by using move()
. The purpose of forward<T>()
is for generic code, when you don't know whether the original value should be pilfered or not. If you were to use move()
in a template, you may accidentally pilfer an l-value. So you use forward<T>()
instead, which will resolve to a harmless pass-through if T
is an l-value type, and will essentially become equivalent to move()
if T
is a non-reference or an r-value reference.
Note that in your edit, your second overload of h
(that is, h(X &t)
) is using forward<>
incorrectly. The type of t
in that situation is X &
, so you should be using forward<X&>(t)
. If you did that, you would find that t
is passed on as an l-value. However, in your two overloads of h
, you can see that in the first, you have an r-value reference, and in the second you have an l-value reference. (There's no template deduction involved, so you know the types.) Therefore, you might as well use move()
directly in your first overload, and not use anything in your second. The purpose of forward<T>()
is to harvest information from the template deduction to determine whether it was bound to (and deduced as) an l-value or an r-value.