Overload resolution between lvalue and rvalue reference parameter when user-defined conversion is required

Viewed 81

Consider the following code:

#include <cstdio>
struct S {
    S(int) {}
};
void f(const S&) { std::puts("const S&"); }
void f(S&&) { std::puts("S&&"); }
int main() {
    f(42);
}

It may be obvious, and GCC and Clang agree, that the S&& overload should be called, but my reading of the standard is that there is actually no rule requiring this to be the case!

Of course, you will say that the call to f has to initialize a temporary S object and that overload resolution prefers to bind an rvalue reference to an rvalue rather than binding an lvalue reference. However, this is what the standard actually says in [over.ics.rank]/(3.2):

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

...

  • S1 and S2 are reference bindings (11.6.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference

...

Thus, this tiebreaker concerning lvalue and rvalue reference bindings only applies to standard conversion sequences. But a user-defined conversion sequence would be required in order to call either overload of f given the int argument. The rule for ranking user-defined conversion sequences is (3.3),

User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor or they initialize the same class in an aggregate initialization and in either case the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2.

Clearly, both conversion sequences (to const S& and S&&) call the same constructor, so the second one could be better than the first if its second standard conversion is better than that of the first. However, [over.ics.ref]/2 says:

When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the referenced type according to 16.3.3.1. Conceptually, this conversion sequence corresponds to copy-initializing a temporary of the referenced type with the argument expression. Any difference in top-level cv-qualification is subsumed by the initialization and does not constitute a conversion.

Based on my reading of this, the implicit conversion sequence for int to const S& is identical to the user-defined conversion sequence for int to const S, which is simply a single user-defined conversion with the second standard conversion being the identity conversion; similarly, the implicit conversion sequence for int to S&& is identical to the user-defined conversion sequence for int to S, which is also a single user-defined conversion with the second standard conversion being the identity conversion.

Therefore, there's no textual basis for the "obvious" choice of the second overload... or is there one that I'm missing?

0 Answers
Related