(constexpr and noexcept are left out, since they seem irrelevant for the purpose of understanding how std::forward behaves.)
Based on my understanding of Scott Meyers' "Effective Modern C++",
a sample implementation of std::move in C++14 is the following
template<typename T>
decltype(auto) move(T&& param) {
return static_cast<remove_reference_t<T>&&>(param);
}
Given the explanation of what a forwarding (or "universal") reference is, this implementation, I think, is pretty clear to me:
- the parameter
paramis of typeT&&, i.e. an rvalue reference or lvalue reference (to whichever the type of the argument is), depending on whether the argument is an rvalue or lvalue in the caller; in other wordsparamcan bind both to rvalues and lvalues (i.e. anything); this is intended, sincemoveshould cast anything to rvalue. decltype(auto)is just the concise way to express the return type based on the actualreturnstatement.- the returned object is the same object
param, casted to an rvalue reference (&&) to whatever the typeTis, once its deduced referenceness is stripped off (the deduction is done onT&&, not on⋯<T>&&).
In short, my understanding of the use of forwarding/universal references in the implementation of move is the following:
- the forwarding/universal reference
T&&is used for the parameter since it is intended to bind to anything; - the return type is an rvalue reference, since
moveis intended to turn anything to rvalue.
It'd be nice to know if my understanding is right so far.
On the other hand, a sample implementation of std::forward in C++14 is the following
template<typename T>
T&& forward(remove_reference_t<T>& param) {
return static_cast<T&&>(param);
}
My understanding is the following:
T&&, the return type,must be a forwarding/universal reference, since we wantis an rvalue reference to whichever template type argument is passed toforwardto return either by rvalue reference or by lvalue reference, hence type deduction takes place on the return type here (unlike what happens formove, where type deduction takes place on the parameter side)forward;- since
Tencodes the lvalue/rvalue-ness of the actual argument which binds the callers' parameter that is passed as argument toforward,Titself can result to beactual_type&oractual_type, henceT&&can be either an lvalue reference or rvalue reference. - The type of
paramis an lvalue reference to whatever the typeTis, once itsdeducedreferenceness is stripped off. Actually instd::forwardtype deduction is disabled on purpose, requiring that the template type argument be passed explicitly.
My doubts are the following.
- The two instances of
forward(two for each type on which is it called, actually) only differ for the return type (rvalue reference when an rvalue is passed, lvalue reference when an lvalue is passed), since in both casesparamis of type lvalue reference to non-constreference-lessT. Isn't the return type something which does not count in overload resolution? (Maybe I've used "overload" improperly, here.) - Since the type of
paramis non-constlvalue reference to reference-lessT, and since an lvalue reference must be to-constin order to bind to an rvalue, how canparambind to an rvalue?
As a side question:
- can
decltype(auto)be used for the return type, as it is done formove?