Overload resolution with templates and rvalue references

205 Views Asked by At

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?

1

There are 1 best solutions below

14
On

When you make the call:

a.f<int&>(a.m);

the compiler has to choose between the following candidates:

template<class U>
T f(U && p);       // #1

template<class U>
T f(U const & p);  // #2

For this overload resolution process, first both templates are transformed for the parameter int&.

Substituting U = int & for #1 gives int & &&, which due to reference collapsing, becomes int &.

Similarly, substituting U = int & for #2 gives int & const &, which again due to reference collapsing, becomes int &.

Now, since both overloads match, partial ordering is used to determine which template to call. Now U const & is more specialized than U &&. This is because U && can bind to all the types that U 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:

a.f(a.m);

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.