(I asked a variation of this question on comp.std.c++ but didn’t get an answer.)
Why does the call to f(arg) in this code call the const ref overload of f?
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
My intuition says that the f(string &&) overload should be chosen, because arg needs to be converted to a temporary no matter what, and the temporary matches the rvalue reference better than the lvalue reference.
This is not what happens in GCC and MSVC (edit: Thanks Sumant: it doesn’t happen in GCC 4.3-4.5). In at least G++ and MSVC, any lvalue does not bind to an rvalue reference argument, even if there is an intermediate temporary created. Indeed, if the const ref overload isn’t present, the compilers diagnose an error. However, writing f(arg + 0) or f(std::string(arg)) does choose the rvalue reference overload as you would expect.
From my reading of the C++0x standard, it seems like the implicit conversion of a const char * to a string should be considered when considering if f(string &&) is viable, just as when passing a const lvalue ref arguments. Section 13.3 (overload resolution) doesn’t differentiate between rvalue refs and const references in too many places. Also, it seems that the rule that prevents lvalues from binding to rvalue references (13.3.3.1.4/3) shouldn’t apply if there’s an intermediate temporary – after all, it’s perfectly safe to move from the temporary.
Is this:
- Me misreading/misunderstand the standard, where the implemented behavior is the intended behavior, and there’s some good reason why my example should behave the way it does?
- A mistake that the compiler vendors have somehow all made? Or a mistake based on common implementation strategies? Or a mistake in e.g. GCC (where this lvalue/rvalue reference binding rule was first implemented), that was copied by other vendors?
- A defect in the standard, or an unintended consequence, or something that should be clarified?
EDIT: I have a follow-on question that is related: C++0x rvalue references – lvalues-rvalue binding
GCC is doing it wrong according the FCD. The FCD says at
8.5.3about reference bindingYour case for the call to the
std::string &&matches none of them, because the initializer is an lvalue. It doesn’t get to the place to create a temporary rvalue, because that toplevel bullet already requires an rvalue.Now, overload resolution doesn’t directly use reference binding to see whether there exist an implicit conversion sequence. Instead, it says at
13.3.3.1.4/2Thus, overload resolution figures out a winner, even though that winner may actually not be able to bind to that argument. For example:
Reference binding at
8.5forbids bitfields to bind to lvalue references. But overload resolution says that the conversion sequence is the one converting toint, thus succeeding even though when the call is made later, the call is ill-formed. Thus my bitfields example is ill-formed. If it was to choose theBversion, it would have succeeded, but needed a user defined conversion.However, there exist two exceptions for that rule. These are
Thus, the following call is valid:
And thus, your example calls the
const T&versionHowever, if you say
f(arg + 0), you create an rvalue, and thus the second function is viable.