我正在努力理解为什么这段代码片段无法编译.
#include <cstdio>
class A {
public:
virtual int potential()=0;
virtual int potential(int arg, int arg2)=0;
};
class B : public A {
public:
int potential() { return 1; }
virtual int potential(int arg, int arg2) { return 2; }
};
class C : public B {
public:
int potential(int arg, int arg2) { return 3; }
};
int main(int argc, char** argv) {
C c;
int value = c.potential();
printf("Got %i\n", value);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我有两个纯虚方法,都potential在抽象超类中命名A.然后子类B定义两者,但是另一个子类C只需要重新定义其中一个方法.
但是,在编译时,只C识别定义的方法,并且potential()看不到(这应该是继承的B):
In function 'int main(int, char**)':
Line 23: error: no matching function for call to 'C::potential()'
compilation terminated due to -Wfatal-errors.
Run Code Online (Sandbox Code Playgroud)
如果我A::potential(int, int)在继承树中一直重命名为其他东西,例如A::somethingElse(int, int),那么代码编译得很好,输出Got 1就像预期的那样.
这已经使用clang,g ++和MSVC的cl进行了验证.
关于发生了什么的任何想法?
Kon*_*lph 26
但是,在编译时,只识别C中定义的方法,并且看不到potential()(这应该是从B继承的).
C++不工作是这样的:因为你实现了一个不同的potential方法(名称相同的方法,但使用不同的参数)中C,另一种方法是隐藏尽可能C而言.
隐藏是因为C++解析(重载)方法名称的方式:当您potential在类的实例(此处c)上调用方法时,C++会在类中搜索是否存在该名称的方法.如果不是这种情况,它继续在基类中搜索.它在层次结构中更进一步,直到找到该名称的至少一个方法.
但在你的情况下,C++不必搜索远:该方法已经存在C,因此它停止搜索.现在C++尝试匹配方法签名.不幸的是,方法签名不匹配,但此时为时已晚:重载解析失败; C++不会搜索可能匹配的其他方法.
有三种解决方案:
用usingC 导入它:
class C : public B {
public:
using B::potential;
int potential(int arg, int arg2) { return 3; }
};
Run Code Online (Sandbox Code Playgroud)从以下基类实例中调用该方法main:
C c;
B& b = c;
int value = b.potential();
Run Code Online (Sandbox Code Playgroud)在main以下位置明确限定名称:
C c;
int value = c.B::potential();
Run Code Online (Sandbox Code Playgroud)Lig*_*ica 11
问题是名称隐藏.
函数重载和函数继承不是最好的朋友.通常你要么 [嗯,三个人都是什么?]:
B派生类中的基类重新实现非重载函数C.C::func 隐藏, B::func因为它具有相同的名称.你正在使用继承和重载,并且你C::func正在隐藏它B::func 并且每次重载它,甚至是那些没有重新实现的C.
这有点古怪的C++混淆,但它很容易解决.
总之,解决的办法是把B::potential()进入的余地C与using语句:
class C : public B {
public:
using B::potential; // brings overloads from B into scope
int potential(int arg, int arg2) { return 3; }
};
Run Code Online (Sandbox Code Playgroud)
我在这里写了一篇文章,深入地展示了这个问题.