As the title says, why named variables calls are resolved to T&&
instead of const T&
functions?
#include <iostream>
template<typename T>
void f(T&& v)
{
std::cout << "void f(T&& v)" << std::endl;
}
template<typename T>
void f(const T& v)
{
std::cout << "void f(const T& v)" << std::endl;
}
int main()
{
int i = 0;
f(1);
f(i);
}
In this case both calls are resolved to first version of f()
, even if i
is named. One solution would be to add also:
template<typename T>
void f(T& v)
{
std::cout << "void f(T& v)" << std::endl;
}
or to change first version to:
template<typename T>
typename std::enable_if<!std::is_reference<T>::value, void>::type f(T&& v)
{
std::cout << "void f(T&& v)" << std::endl;
}
but I want to understand the reasons behind this decision.
The deduction is T = int &
, which means f(T &&) == f(int &)
. The overload resolution rules ([over.ics.rank/13.3.3.2]) say that this is is a strictly better match than f(int const &)
. Both are classified as an "exact match" (binding a value to a reference), but the less CV-qualified reference is preferred.
A less standardese-encumbered answer would be to recognise that in the declaration:
template<typename T>
void f(T&& v)
Because T
is a deduced type, T&& v
is what Scott Meyers calls a universal reference, which can bind to both rvalues and lvalues. T
can be deduced to be a reference type, so you are actually calling f<int&>(int& && v)
, at which point the reference-collapsing rules come into effect, making the nominal signature of this function f<int&>(int& v)
which, as the previous answer noted, is a better match for a non-const int
argument.
In Meyers' upcoming Effective Modern C++ he has the following items:
Item 31 tells you what not to do. Item 30 suggests what you could do instead, write only one overload of f
, and perfectly forward v
with std::forward
:
template<typename T>
void f(T&& v)
{
std::cout << "void f(T&& v)" << std::endl;
if (std::is_rvalue_reference<decltype(v)>::value)
std::cout << "rvalue" << std::endl;
else
std::cout << "lvalue" << std::endl;
std::cout << "v = " << std::forward<T>(v) << std::endl;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With