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

1

8

[over.ics.rank]/4:

  • [..]
  • (4.3) If class B is derived directly or indirectly from class A, conversion of B* to A* is better than conversion of B* to void*, and conversion of A* to void* is better than conversion of B* to void*.

So if I have:

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

void f(A*);
void f(void*);

int main()
{
   B *bptr = new B();
   f(bptr);
}

The call f(bptr) would prefer the overload f(A*) over f(void*).

But in the second case: conversion of A* to void* is better than conversion of B* to void*. How this conversion can occur? Can you give me an example that triggers this case?


For some reasons I can't finds out a case or an example in which this case is applied. It seems like comparing two unrelated things to each other. But I encounter more in the bullet 4.4.

You can check the whole thing from cppreference para 4:

  1. If Mid is derived (directly or indirectly) from Base, and Derived is derived (directly or indirectly) from Mid
  • a) Derived* to Mid* is better than Derived* to Base*
  • b) Derived to Mid& or Mid&& is better than Derived to Base& or Base&&
  • c) Base::* to Mid::* is better than Base::* to Derived::*
  • d) Derived to Mid is better than Derived to Base
  • e) Mid* to Base* is better than Derived* to Base*
  • f) Mid to Base& or Base&& is better than Derived to Base& or Base&&
  • g) Mid::* to Derived::* is better than Base::* to Derived::*
  • h) Mid to Base is better than Derived to Base
Kory answered 6/7, 2022 at 14:11 Comment(5)
Perhaps if you only had the void* overload, it would go B* -> A* -> void*? This might matter for deep inheritances where B* and A* point to different places.Cristen
Or multiple arguments: void f(A*, void*); void f(void*, B*); A *aptr = new A(); f(aptr, bptr); would choose f(void*, B*)?Psychologize
void* what's that? Kidding aside, just curious, what use do you have for a void* in C++? (unless it is for communicating with some legacy api). Or is it a bit of language laywering?Leitmotif
@GoswinvonBrederlow Overload resolution according to the standard never considers ranking of sequences between arguments in different positions. That often makes calls that seem to have an obviously preferable overload ambiguous, which is why some compilers implement some reasonable resolutions for these cases, but they are not standard-conforming.Strapped
@FilipeRodrigues A standard conversion sequence contains only one pointer conversion. B* -> A* -> void* isn't possible to begin with.Strapped
S
5

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*().

Strapped answered 6/7, 2022 at 14:58 Comment(1)
@John Yes, exactly.Strapped

© 2022 - 2024 — McMap. All rights reserved.