conversion precedence in c++

When overload resolution is performed to select the best candidate out of all viable overloads – the compiler ranks all the conversion sequences for each argument for each candidate. For a function to win (be selected as the best candidate), its conversion ranks for each argument have to be better than or equal to every other function’s conversion ranks for that argument AND at least one conversion rank has to be better than all the other function’s conversion ranks for a certain argument.

The user defined conversion (which uses either a constructor or a cast operator) has one of the worst possible ranks (only the ellipsis has a worse rank). The integral-floating conversion has a better rank (see below for a list of the rankings).

Thus, the compiler prefers converting an int -> double (using a standard conversion) than converting an int -> A (using a user defined conversion), and therefore it selects f1.

Edit: Although “Overload resolution” works in the background and most of the time does exactly what you would expect (i.e. most programmers won’t need to delve into the technicalities) – if you do want to go deeper (but be warned that some of the darker corners of overload resolution are considered to be one of the trickiest aspects for compiler writers to get exactly right in a C++ compiler) refer to the excellent
C++ Templates: The Complete Guide by David Vandevoorde and Nicolai M. Josuttis
which provides, in appendix B, one of the best introductions to the machinery behind overload resolution that I have read.

Here is an excerpt from B.2:

Overload resolution ranks the viable
candidate functions by comparing how
each argument of the call matches the
corresponding parameter of the
candidates. For one candidate to be
considered better than another, the
better candidate cannot have any of
its parameters be a worse match than
the corresponding parameter in the
other candidate.

Given this first principle, we are
left with specifying how well a given
argument matches the corresponding
parameter of a viable candidate. As a
first approximation we can rank the
possible matches as follows (from best
to worst):

Perfect match. The parameter has the
type of the expression, or it has a
type that is a reference to the type
of the expression (possibly with added
const and/or volatile qualifiers).

Match with minor adjustments. This
includes, for example, the decay of an
array variable to a pointer to its
first element, or the addition of
const to match an argument of type
int** to a parameter of type int
const* const*.

Match with promotion. Promotion is a
kind of implicit conversion that
includes the conversion of small
integral types (such as bool, char,
short, and sometimes enumerations) to
int, unsigned int, long or unsigned
long, and the conversion of float to
double.

Match with standard conversions only.
This includes any sort of standard
conversion (such as int to float) but
excludes the implicit call to a
conversion operator or a converting
constructor.

Match with user-defined conversions.
This allows any kind of implicit
conversion.

Match with ellipsis. An ellipsis
parameter can match almost any type
(but non-POD class types result in
undefined behavior).

But that’s just the beginning – if you are intrigued – I urge you to read the book and then the relevant portions of the standard 🙂

Leave a Comment