将指向基类的指针转换为void比将指向派生类的指针转换为void更好。

8

[over.ics.rank]/4:

  • [..]
  • (4.3) 如果类B直接或间接地派生自类A,则将B*转换为A*优于将B*转换为void*,而将A*转换为void*则优于将B*转换为void*。

因此,如果有以下代码:

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

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

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

调用 f(bptr) 会优先选择重载 f(A*) 而不是 f(void*)

但在第二种情况下:A*到void*的转换比B*到void*的转换更好。这种转换如何发生?你能给我一个触发此情况的示例吗?


出于某些原因,我找不到一个应用此情况的案例或示例。这似乎像是将两个无关的事物进行比较。但我在第4.4段中遇到了更多信息。

你可以从cppreference的第4段检查整个内容:

  1. 如果Mid是从Base派生的(直接或间接),而Derived也是从Mid派生的
  • a)Derived*到Mid*比Derived*到Base*更好
  • b)Derived到Mid&或Mid&&比Derived到Base&或Base&&更好
  • c)Base::*到Mid::*比Base::*到Derived:: *更好
  • d)Derived到Mid比Derived到Base更好
  • e)Mid*到Base*比Derived*到Base*更好
  • f)Mid到Base&或Base&&比Derived到Base&或Base&&更好
  • g)Mid::*到Derived::*比Base::*到Derived:: *更好
  • h)Mid到Base比Derived到Base更好

也许如果你只有 void* 重载,它会走 B* -> A* -> void* 的路线?在深度继承中,B*A* 指向不同位置时,这可能很重要。 - Filipe Rodrigues
或者多个参数:void f(A*,void*); void f(void*,B*); A *aptr = new A(); f(aptr,bptr); 会选择 f(void*,B*) 吗? - Goswin von Brederlow
void* 是什么?开玩笑的,只是好奇,在 C++ 中你有什么用途需要使用 void*?(除非它是用于与某些遗留 API 进行通信)。或者这只是一种语言上的技巧? - Pepijn Kramer
1
@GoswinvonBrederlow 根据标准,重载决议从不考虑不同位置参数序列之间的排名。这经常导致调用似乎具有明显优先级的重载变得模糊不清,这就是为什么一些编译器对这些情况实现了一些合理的解决方案,但它们并不符合标准。 - user17732522
@FilipeRodrigues 一个标准的转换序列只包含一个指针转换。B* -> A* -> void*从一开始就是不可能的。 - user17732522
1个回答

5
你需要比较不同的源类型(在和中的和),这种情况只会出现在用户定义转换初始化的重载分辨上下文中,而不是函数调用的重载分辨上下文中。请参阅[over.ics.rank]/4结尾的注释
以下是引用该短语相关的示例:
struct A {};
struct B : A {};

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

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

在这里,x是通过选择根据[over.match.conv](参见[dcl.init.general]/16.7)选择的转换函数进行初始化的。所有从C转换到任何可通过标准转换序列转换为void*的类型的转换运算符都被考虑。

然后,根据转换函数后面的标准转换序列中哪个更好来选择最佳转换函数(参见[over.match.best.general]/2.2)。

你引用的短语告诉我们A* -> void*B* -> void*更好。因此,在这里用于初始化x的转换函数是operator A*()


1
@John 是的,完全正确。 - user17732522

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接