I've been reading Effective Modern C++ and the following thing caught my attention:
In Item 28 Scott writes:
Together, these observations about universal references and lvalue/rvalue encoding mean that for this template
template<typename T> void func(T&& param);the deduced template parameter T will encode whether the argument passed to param was an lvalue or an rvalue. The encoding mechanism is simple. When an lvalue is passed as an argument, T is deduced to be an lvalue reference. When an rvalue is passed, T is deduced to be a non-reference. (Note the asymmetry: lvalues are encoded as lvalue references, but rvalues are encoded as non-references.)
Can somebody explain why such encoding mechanism was chosen?
I mean if we will follow reference collapsing rules than usage of aforementioned template with rvalue yields rvalue reference. And as far as I can tell everything would work just the same if it were deduced as rvalue reference. Why is it encoded as non-reference?
Let's say you need to pass
paramaround. In fact, you need to store it for lifetime purposes. As is, you'd just useT:This works because if
paramgot passed in by lvalue,Twould be an lvalue reference type, and we're just copying the reference. Ifparamgot passed in by rvalue, we need to take ownership -Tis a non-reference type, so we end up move-constructing intos.The fact that I can just use
There as the template argument forstore, instead ofstd::conditional_t<std::is_lvalue_reference<T>::value, T, std::remove_reference_t<T>>is probably not an accident.