5
votes

My understanding of overload resolution is that 'T&&' is generally a better match than 'const T&'. However, I'm seeing some inconsistent behavior among compilers with this trivial example:

#include <iostream>

void Func(const double& a)
{
    (void)a;
    std::cout << "[lvalue]\n";
}

void Func(double&& a)
{
    (void)a;
    std::cout << "[rvalue]\n";
}

template <typename T>
void TFunc(T&& a)
{
    Func(a);
}

int main ()
{
    TFunc(5.5f);

    return 0;
}

Clang 3.2 will print [rvalue]. However, the VS2013 32bit/64bit compilers will print [lvalue]. If I change '5.5f' to '5.5', then both compilers will print [lvalue].

I can understand why the VS compiler chooses the 'const double&' version since I don't have a std::forward call to preserve the '&&' of the parameter. However, I still don't understand what makes clang think that the '&&' overload is the better choice.

Why does adding an implicit float to double conversion affect the behavior in clang? Who is right?

1

1 Answers

8
votes

When you call with 5.5f, T is float, and the Func call effectively becomes Func(double(a)). The argument is a temporary, and so rvalue overload should be chosen.

When you call with 5.5, T is double, and no temporary is created in a call to Func(a). A named variable cannot bind to an rvalue reference, so lvalue overload is the only choice.

MSVC has a long-standing misfeature that allows temporaries to bind to non-const lvalue references. This is why it incorrectly chooses lvalue overload even in the first case. Try compiling with /Za (disable language extensions) - I believe it would match clang's behavior then.