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



我正在试验这段代码

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)调用双版本。由于派生继承了int方法,并有自己的双方法,多态性不会规定2与父类匹配吗?谢谢。

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

object->foo()总是在编译时解析符号-重载operator()或方法的成员变量。virtual只是延迟选择"主体";方法的。签名总是固定的,当然也包括返回值。否则类型系统就会中断。这就是为什么不能有虚函数模板的原因之一。

你实际经历的是名称隐藏和重载解析。

对于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))被选为更好的匹配。

读取过载解析。答案在Details:

如果任何候选函数是成员函数(静态或非静态),但不是构造函数,则将其视为具有一个额外的形参(隐式对象形参),该形参表示调用它们的对象,并且出现在第一个实际形参之前。

方法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)三个函数之间进行解析,并打印int

最新更新