This overload resolution behavior baffles me:
#include "stdio.h"
template<class T>
class C
{
public:
C(T v): m(v) {};
T m;
template<class U>
T f(U &&p)
{
printf("rRef called.\n");
return p;
}
template<class U>
T f(const U &p)
{
printf("const Ref called.\n");
return p;
}
};
int main()
{
C<int> a(5);
a.f<int&>(a.m);
a.f(a.m);
return 0;
}
Outputs:
const Ref called.
rRef called.
When debugging in gdb or Visual Studio, both debuggers show
int C<int>::f<int &>()
called in both cases, but the explicit template resolution resolves to the expected const ref, while the second resolves to a rvalue reference. Why? Why doesn't the compiler even try
int C<int>::f<int>()
which I thought would be the obvious match?
How can a rvalue reference bind to a member value? Isn't a.m
a lvalue?
When you make the call:
the compiler has to choose between the following candidates:
For this overload resolution process, first both templates are transformed for the parameter
int&
.Substituting
U = int &
for#1
givesint & &&
, which due to reference collapsing, becomesint &
.Similarly, substituting
U = int &
for#2
givesint & const &
, which again due to reference collapsing, becomesint &
.Now, since both overloads match, partial ordering is used to determine which template to call. Now
U const &
is more specialized thanU &&
. This is becauseU &&
can bind to all the types thatU const &
can, but the converse is not true.Hence, since
#2
is more specialized, it wins in overload resolution, and gets called.In this call:
the template parameter is not specified. This means the parameter of
#1
is considered to be a forwarding reference, and this matches all types that are passed in, and so#1
gets called.