Search code examples
c++language-lawyeroverload-resolution

Why does a better standard conversion sequence followed by a user-defined conversion not make an overload better?


This is a follow-up question to how to avoid implicit cast from `double` to `int`?. Say I have the following code:

template <class T>
struct wrapper {
    wrapper(const T&);
};

void f(wrapper<int>);

void f(wrapper<double>);

int main () {
    f(1); // error: ambiguous call to overloaded function
}

See live example at Compiler Explorer; note that the behavior is the same for all major compilers

How come f(1) is ambiguous? There are two conversion sequences:

  • 1 -> const int& via qualification conversion and reference binding -> wrapper<int> via converting constructor
  • 1 -> const double& via floating point conversion, temporary materialization, qualification conversion, and reference binding -> wrapper<double> via converting constructor

The first conversion sequence looks better, so how come the first overload doesn't win?


Solution

  • The entire implicit conversion sequence consists of a user-defined conversion sequence:

    A well-formed implicit conversion sequence is one of the following forms:

    - [over.best.ics.general] p3

    A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion followed by a second standard conversion sequence.

    - [over.ics.user] p1

    The standard conversion sequence int -> const int& is better than int -> const double&, however, this is not considered. Both user-defined conversion sequences int -> wrapper<int> and int -> wrapper<double> are equally ranked by [over.ics.rank] p2, and the only distinction between user-defined conversion sequences in [over.ics.rank] p3.3 applies to the second standard conversion only.

    Contrary to intuition:

    • A conversion wrapper<int> -> int would be better than wrapper<int> -> double if wrapper<T> had an operator T conversion function.
    • However, the conversions int -> wrapper<int> and int -> wrapper<double> are equally ranked.