How to Compare Two Standard Conversion Sequences Use the Rank of Contained Conversions

How to determine two implicit conversion sequences preferred than each other for overload resolution?

Cppreference is a useful reference site, but it is not the C++ standard. It has mostly accurate information, but in boiling down the complexities of the standard, some details can be lost around the margins.

I will be citing C++17 (since I don't have a C++11 website I can link to), but the wording has not meaningfully changed.

[over.best.ics]/3 defines an implicit conversion sequence as:

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

  • a standard conversion sequence,
  • a user-defined conversion sequence, or
  • an ellipsis conversion sequence.

This is more-or-less what Cppreference says, but here it is made more explicit that you either have precisely one standard conversion sequence or you have precisely one user-defined conversion. The user-defined conversion itself includes two standard conversion sequences:

A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion ([class.conv]) followed by a second standard conversion sequence.

Cppreference makes it seem like you can have two standard conversion sequences without a user-defined conversion, but you cannot. If there is no user-defined conversion in play, you have only one standard conversion.

Don't try to read Cppreference as the legal definition of C++. Think of it more as "C++ works mostly like this". If you need legalistic detail (and in most cases, you don't. Especially around overload resolution. Knowing too much about this can make you a bad programmer), then you have to go to the standard.

Is an identity conversion of pointer type be considered as qualification conversion when ranking

The standard conversion sequence from int* to int* does not contain a qualification conversion.

This is made explicit by [over.ics.best.general]/8:

If no conversions are required to match an argument to a parameter type, the implicit conversion sequence is the standard conversion sequence consisting of the identity conversion ([over.ics.scs]).

It can also be inferred from other paragraphs. For example, see [over.best.ics.general]/1:

An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called.
The sequence of conversions is an implicit conversion as defined in [conv], which means it is governed by the rules for initialization of an object or reference by a single expression ([dcl.init], [dcl.init.ref]).

and [over.ics.best.general]/6:

When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression.
The implicit conversion sequence is the one required to convert the argument expression to a prvalue of the type of the parameter.

...

That is, the correct implicit conversion sequence for int* -> int* is the one that, according to the rules of the language, is normally formed when int* is copy-initialized from int*. In this case, [dcl.init.general]/16.9 applies:

Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression.
A standard conversion sequence ([conv]) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered.
If the conversion cannot be done, the initialization is ill-formed.
When initializing a bit-field with a value that it cannot represent, the resulting value of the bit-field is implementation-defined.

No conversion is necessary since int* and int* are already the same type, so no conversion is done. It is not a qualification conversion from int* to itself.

This means [over.ics.rank]/3.2.1 is certainly dispositive: S1 is the identity conversion sequence (not an implicit conversion sequence consisting of a redundant qualification conversion) so it's a subsequence of S2.

Distinguishing between user-defined conversion sequences by the initial standard conversion sequence

The trick here is that converting from a class type to a non-class type doesn't actually rank any user-defined conversions as implicit conversion sequences.

struct A {
operator int();
operator char() const;
} a;
void foo(double);
int main() {
foo(a);
}

In the expression foo(a), foo obviously names a non-overloaded non-member function. The call requires copy-initializing (8.5p14) the function parameter, of type double, using the single expression a, which is an lvalue of class type A.

Since the destination type double is not a cv-qualified class type but the source type A is, the candidate functions are defined by section 13.3.1.5, with S=A and T=double. Only conversion functions in class A and any base classes of A are considered. A conversion function is in the set of candidates if:

  • It is not hidden in class A, and
  • It is not explicit (since the context is copy-initialization), and
  • A standard conversion sequence can convert the function's return type (not including any reference qualifiers) to the destination type double.

Okay, both conversion functions qualify, so the candidate functions are

A::operator int();        // F1
A::operator char() const; // F2

Using the rules from 13.3.1p4, each function has the implicit object parameter as the only thing in its parameter list. F1's parameter list is "(lvalue reference to A)" and F2's parameter list is "(lvalue reference to const A)".

Next we check that the functions are viable (13.3.2). Each function has one type in its parameter list, and there is one argument. Is there an implicit conversion sequence for each argument/parameter pair? Sure:

  • ICS1(F1): Bind the implicit object parameter (type lvalue reference to A) to expression a (lvalue of type A).
  • ICS1(F2): Bind the implicit object parameter (type lvalue reference to const A) to expression a (lvalue of type A).

Since there's no derived-to-base conversion going on, these reference bindings are considered special cases of the identity conversion (13.3.3.1.4p1). Yup, both functions are viable.

Now we have to determine if one implicit conversion sequence is better than the other. This falls under the fifth sub-item in the big list in 13.3.3.2p3: both are reference bindings to the same type except for top-level cv-qualifiers. Since the referenced type for ICS1(F2) is more cv-qualified than the referenced type for ICS1(F1), ICS1(F1) is better than ICS1(F2).

Therefore F1, or A::operator int(), is the most viable function. And no user-defined conversions (with the strict definition of a type of ICS composed of SCS + (converting constructor or conversion function) + SCS) were even to be compared.

Now if foo were overloaded, user-defined conversions on the argument a would need to be compared. So then the user-defined conversion (identity + A::operator int() + int to double) would be compared to other implicit conversion sequences.

Why does my application still run after closing main window?

You should call the setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); in your JFrame.

Example code:

public static void main(String[] args) {
Runnable guiCreator = new Runnable() {
public void run() {
JFrame fenster = new JFrame("Hallo Welt mit Swing");
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fenster.setVisible(true);
}
};
SwingUtilities.invokeLater(guiCreator);
}

How conversion of pointer-to-base to void is better than pointer-to-derived to void conversion

The situation where you have to compare different possible source types (A* vs B* in A* -> void* and B* -> void*) can only happen in the context of overload resolution for initialization by user-defined conversion, not in the context of overload resolution for a function call. See also the note at the end of [over.ics.rank]/4.

Here is an example where the quoted phrase is relevant:

struct A {};
struct B : A {};

struct C {
operator A*();
operator B*();
};

int main() {
void* x = C{};
}

Here x is initialized by a conversion function chosen according to [over.match.conv] (see [dcl.init.general]/16.7). All conversion operators converting from C to any type which is convertible to void* by a standard conversion sequence are considered.

Then the best conversion function is chosen according to which of the standard conversion sequences following the conversion function is better (see [over.match.best.general]/2.2).

The phrase you quote tells us that A* -> void* is better than B* -> void*. So the conversion function which will be used here to initialize x is operator A*().



Related Topics



Leave a reply



Submit