23.5.2.1 Reference Deduction[Q]----The C++ Programming Language 4th Edition
It can be useful to have different actions taken for lvalues and rvalues. Consider a class for holding
an {integer,pointer} pair:
template<typename T>
class Xref {
public:
Xref(int i, T∗ p) // store a pointer: Xref is the owner
:index{i}, elem{p}, owner{true}
{ }
Xref(int i, T& r) // store a pointer to r, owned by someone else
:index{i}, elem{&r}, owner{false}
{ }
Xref(int i, T&& r) // move r into Xref, Xref is the owner
:index{i}, elem{new T{move(r)}}, owner{true}
{ }
˜Xref()
{
if(owned) delete elem;
}
// ...
private:
int index;
T∗ elem;
bool owned;
};
So:
string x {"There and back again"};
Xref<string> r1 {7,"Here"}; // r1 owns a copy of str ing{"Here"}
Xref<string> r2 {9,x}; // r2 just refers to x
Xref<string> r3 {3,new string{"There"}}; // r3 owns the string{"There"}
Here, r1 picks Xref(int,string&&) because x is an rvalue. Similarly, r2 picks Xref(int,string&) because x
is an lvalue.
Lvalues and rvalues are distinguished by template argument deduction: an lvalue of type X is
deduced as an X& and an rvalue as X. This differs from the binding of values to non-template argument
rvalue references (§12.2.1) but is especially useful for argument forwarding (§35.5.1). Consider
writing a factory function that make Xrefs on the free store and returns unique_ptrs to them:
template<typename T>
T&& std::forward(typename remove_reference<T>::type& t) noexcept; // §35.5.1
template<typename T>
T&& std::forward(typename remove_reference<T>::type&& t) noexcept;
template<typename TT, typename A>
unique_ptr<TT> make_unique(int i, A&& a) // simple var iant of make_shared (§34.3.2)
{
return unique_ptr<TT>{new TT{i,forward<A>(a)}};
}
We want make_unique<T>(arg) to construct a T from an arg without making any spurious copies. To
do that, it is essential that the lvalue/rvalue distinction is maintained. Consider:
auto p1 = make_unique<Xref<string>>(7,"Here");
"Here" is an rvalue, so forward(string&&) is called, passing along an rvalue, so that Xref(int,string&&)
is called to move from the string holding "Here".
The more interesting (subtle) case is:
auto p2 = make_unique<Xref<string>>(9,x);
Here, x is an lvalue, so forward(string&) is called, passing along an lvalue: forward()’s T is deduced to
string& so that the return value becomes string& &&, which means string& (§7.7.3). Thus,
Xref(int,string&) is called for the lvalue x, so that x is copied.
Unfortunately, make_unique() is not part of the standard library, but it is widely supported nevertheless.
Defining a make_unique() that can take arbitrary arguments is relatively easy using a variadic
template for forwarding (§28.6.3).