派生 class 中定义的 c++ 多态方法匹配应与父级匹配的类型

c++ polymorphic method defined in derived class matches type that should match to the parent

我正在试验这段代码

class Base
{
public:
virtual void foo(int) {
// void foo(int) {
    cout << "int" << endl;
}
};

class Derived : public Base
{
public:
void foo(double) {
    cout << "double" << endl;
}
};

int main()
{
    Base* p = new Derived;
    p->foo(2.1);
    
    Derived d;
    d.foo(2);  // why isn't this double?
    
    return 0;
}

也可以在此处的在线编辑器中使用https://onlinegdb.com/s8NwhfG_Yy

我得到

int
double

我不明白为什么d.foo(2) 调用双版本。由于 Derived 继承了 int 方法并有自己的 double 方法,多态性不会要求 2 与父对象匹配吗?谢谢

阅读Overload resolution。答案在详情:

If any candidate function is a member function (static or non-static), but not a constructor, it is treated as if it has an extra parameter (implicit object parameter) which represents the object for which they are called and appears before the first of the actual parameters.

方法 void foo(int)void foo(double) 之间的选择就像函数 void foo(Base, int)void foo(Derived, double) 之间的选择一样。调用 d.foo(2) 时的第二个函数排名更高。

如果在Derived中添加using Base::foo;,解析将在void foo(Base, int)void foo(Derived, int)void foo(Derived, double)三个函数之间进行,[=20] =] 将被打印出来。

调用虚拟方法时的多态性与符号和重载解析正交。前者发生在 运行 时间,其余发生在编译时。

object->foo() 总是在编译时解析符号 - 重载 operator() 或方法的成员变量。 virtual 仅延迟选择方法的“主体”。签名总是固定的,当然包括 return 值。否则类型系统会崩溃。这也是不能有虚函数模板的原因之一

您实际遇到的是名称隐藏和重载解析。

对于Base* p; p->foo(2.1),可能的符号候选列表只有Base::foo(int)。编译器无法知道 p 指向 Derived(通常),因为选择必须在编译时完成。由于 int 可隐式转换为 double,因此选择 foo(int)。因为该方法是虚拟的,如果 Derived 提供它自己的 foo(int) override,它会在 运行 时被调用而不是 Base::foo(int)

对于Derived*d; d->foo(2),编译器首先在Derived中查找符号foo。因为有 foo(double)foo(2) 一样有效,所以选择它。如果没有有效的候选者,编译器才会查看基数 类。如果有 Derived::foo(int),可能是虚拟的,编译器会选择它,因为它是更好的匹配。

您可以通过写入

来禁用名称隐藏
class Derived: public Base{
    using Base::foo;
};

它将所有 Base::foo 方法注入 Derived 范围。在此之后,Base::foo(int)(现在实际上是 Derived::foo(int))被选为更好的匹配。