Let's say I have:
template<class T>
struct NodeBase
{
T value;
NodeBase(T &&value)
: value(value) { }
};
and I inherit from it:
template<class T>
struct Node : public NodeBase<T>
{
Node(T &&value)
: NodeBase( WHAT_GOES_HERE (value)) { }
};
Should WHAT_GOES_HERE
be std::move
or std::forward<T>
? Why?
Probably neither.
What I suspect you should have is:
unless you are doing something exceedingly strange.
By exceedingly strange, it means that if your
T
is anint
, you want to only accept moved-from values into yourNode
, but if yourT
is anint&
, you accept only non-const lvalueint
s, and ifT
is anint const&
, you accept any value convertible toint
.This would be a strange set of requirements to place on the constructor of
NodeBase
. I can think of situations where this might be the case, but they are not common.Assuming you simply want
NodeBase
to store that data, takingT&&
in the constructor is not the right thing to do -- if you are storing anint
inNodeBase
, you probably are willing to make copies of thatint
, instead of only accepting moved-fromint
s.The above code does exactly that -- it allows anything that could be stored in the
NodeBase
to be passed on up to saidNodeBase
, with perfect forwarding.On the other hand, if you actually want the strange set of construction restrictions, then this is not the right answer for you. I've used that when I was building the a
template
type that was built from a universal reference argument, and I did want to restrict the passed in type to match the universal reference argument exactly, and store it iff the argument was an rvalue reference, and otherwise keep a reference to it.T
isn't deduced in your example. TheT
is a class template parameter, not a function template parameter. So assuming you will not use a reference type as T,T&&
will be an rvalue-reference to T, so it will only bind to rvalues of type T. these will be safe to move so you can usestd::move
here.std::forward
is normally only for places where you have a deduced type that may either be an lvalue-reference or rvalue-reference.Because
T&&
may actually be either an lvalue-reference or rvalue-reference. This is only becauseT
is deduced in this context.Since in the implementation of the constructor of
Node<T>
it is unknown whetherT
is a plain type (i.e. not a reference), or a reference,is suitable.
std::forward<T>(value)
is the right choice whenever it isn't known whetherT &&
binds to an rvalue or an lvalue. This is the case here because in the constructor we don't know whetherT &&
is equivalent toU &&
for some plain typeU
, or equivalent toU & &&
.It doesn't matter whether
T
is deduced in the function call that usesstd::forward
or determined at a different time (such as in your example, whereT
is determined at the time when theNode
template is instantiated).std::forward<T>(value)
will call the inherited constructor in the same way as if the base class constructor had been called directly by the caller. I.e., it will call it on an lvalue whenvalue
is an lvalue, and on an rvalue whenvalue
is an rvalue.