user3188445
user3188445

Reputation: 4774

how can a standard conversion include a user-defined conversion?

I'm trying to understand the following language from cppreference.com (emphasis mine):

Each type of standard conversion sequence is assigned one of three ranks:

  1. Exact match: no conversion required, lvalue-to-rvalue conversion, qualification conversion, function pointer conversion (since C++17), user-defined conversion of class type to the same class
  2. Promotion: integral promotion, floating-point promotion
  3. Conversion: integral conversion, floating-point conversion, floating-integral conversion, pointer conversion, pointer-to-member conversion, boolean conversion, user-defined conversion of a derived class to its base

From the discussion of implicit conversions, it sounds like a standard conversion sequence cannot include a user-defined conversion. Rather, an implicit conversion consists of either only standard conversions, or a single user-defined conversion sandwiched between two (possibly zero-length) standard conversion sequences.

So then how can it be that a standard conversion might include a user-defined conversion? And what's an example of a user-defined conversion of a class type to the same class, or a user-defined conversion of a class to its base? Is it the case, for example, that a user-defined copy constructor is considered a standard conversion, and so can be paired with a true user-defined conversion in the same implicit conversion sequence? Can someone give a simple example of a user-defined conversion being treated as a standard conversion like this, or else explain how I'm misreading the quoted text?

Upvotes: 0

Views: 101

Answers (1)

Brian Bi
Brian Bi

Reputation: 119382

Overload resolution occurs in a few steps. One of those steps is to form an implicit conversion sequence from each argument to the corresponding parameter type for each candidate.

When overload resolution completes successfully, then the actual call will be generated using the best viable candidate. For the actual call, there will be an actual implicit conversion from each argument to the corresponding parameter type of the best viable candidate.

Although implicit conversion sequence sounds very similar to implicit conversion, they are not the same thing. An implicit conversion sequence is created during overload resolution and used only for the purposes of overload resolution. It is discarded when overload resolution is complete. An implicit conversion sequence cannot be evaluated. An implicit conversion sequence looks like a description of an implicit conversion, but isn't truly one. It's not literally a set of instructions on how to perform an implicit conversion.

(But an implicit conversion sequence usually does match the actual implicit conversion. After all, overload resolution ought to be based on something similar to what will actually happen if a particular overload is called.)

A "standard conversion sequence" (a fictitious entity in the overload resolution process) does not have to represent a "standard conversion" (something that can actually be evaluated according to the rules in [conv]).

For example, there is no standard conversion from an argument of class type to a base class type, but when forming an implicit conversion sequence, we simply invent a standard conversion sequence called a "derived-to-base conversion".

As for the case of converting an argument to its own class type, consider the overload resolution in this example:

struct T {};
void f(T);

int main() {
    T t;
    f(t);
}

Here, we consider the implicit conversion sequence from t to T to be the identity conversion, which is a standard conversion sequence, because that is just what the rules of overload resolution tell us. What exactly actually happens if f were to be called is ignored during overload resolution. In most cases, it would be a call to the copy constructor of T. So a standard conversion sequence can correspond to a function call in the event that the overload is chosen, but the function call isn't actually part of the standard conversion sequence. The standard conversion sequence itself, in this case, is basically just "do nothing".

Again, when the actual call to f is generated, it will use the copy constructor of T, but just because the actual conversion uses a function does not mean that the implicit conversion sequence, formed during overload resolution, involves a user-defined conversion. The implicit conversion sequence was the identity conversion because the rules of the language said so and for no other reason.

If the argument had a type U derived from T, similarly, the overload resolution process simply assumes that the conversion will succeed somehow in case the candidate is actually chosen, but is not concerned with how that conversion will actually be done. The standard conversion sequence in this case just says "convert the derived type U to the base class T" and is given "conversion" rank, that is, it's worse than a conversion from a type to itself (which would have "exact match" rank). The implicit conversion sequence does not involve any user-defined conversion.

A standard conversion sequence never involves a user-defined conversion, and is always better than any user-defined conversion sequence. That is true even for standard conversion sequences involving class types.

Upvotes: 2

Related Questions