0

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?

3
  • It might be better to read the standard on these points, rather than the cppreference re-interpretation of standard. Consider adding language-lawyer tag to the question, if you think it might help clarify understanding. Commented Oct 5, 2024 at 3:02
  • Looking at the table in over.ics.scs, there is not mention of user-defined conversions. Commented Oct 5, 2024 at 7:48
  • The two uses of user-defined conversion in the cited text are taken from over.ics.user#4. Commented Oct 5, 2024 at 14:32

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

10 Comments

"implicit conversion sequence from t to T..." A value(t) cannot be converted to a type(T). So the correct way to say this would be "implicit conversion sequence from T to T"
@user12002570 Implicit conversion sequences are always from an argument (an expression or braced-init-list) to a type. [over.best.ics.general]/1
I think that is a language use defect(editorial/language defect etc). A value can only be converted to a value. What [over.best.ics.general] mean is that a value is converted to a value of the type of parameter. And basically the result(of that conversion) is a value(of a certain type) not a type itself. I'll submit a cwg issue. Also since the function parameter need a "value" as argument not a type so the result of the implicit conversion cannot be a type.
Another example why this converting a value to a type doesn't make sense is that say we have template<typename T> void f();. Here the template parameter expects a type-argument so we can't provide a "value" and expect it to be converted to a "type" using some implicit conversion(as the current rule says a value is converted to a type).
@user12002570 I can see that you haven't understood my answer. An implicit conversion sequence yields a type, not a value because producing a value is not the goal of forming an implicit conversion sequence. The implicit conversion sequence is used for overload resolution. The actual implicit conversion, which produces a value, comes later.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.