查看以下代码:
struct A {
public:
virtual void f(){std::cout << "in A";};
};
struct B : A{
public:
virtual void f(){std::cout << "in B";};
int a;
};
struct C : B{
using A::f;
void test(){f();}
};
int main()
{
C c;
c.f(); // calls B::f, the final overrider
c.C::f(); // calls A::f because of the using-declaration
c.test(); //calls B::f
return 0;
}
根据我的理解,B::f()
中的C
应该隐藏使用声明带来A::f()
的{{1}};如果是的话,为什么C
仍然会调用c.C::f()
?
如果A::f()
调用c.C::f()
,那应该意味着在A::f()
的范围内,C
应始终引用f()
,这就是函数使用声明。那么为什么在A::f()
中,对C::test()
的调用仍然评估为f()
?
答案 0 :(得分:29)
非常好的问题,一个复杂的名称查找案例。
基本上,当在f
的范围内查找名称C
时,由于using声明,总是找到A::f
。因此,c.f()
中的所有来电c.C::f()
,f()
和C::test()
都会将名称f
解析为A::f
。
接下来是虚拟调度。如果通过非限定名称调用虚函数,则会发生动态调度并调用最终的覆盖。这涵盖了c.f()
和f()
中的C::test()
来电,因为这些都是不合格的。
调用c.C::f()
使用f
的限定名称,该名称禁止动态调度以及直接调用名称解析的函数。由于该函数是A::f
(由于使用声明),A::f
被称为非虚拟。相关规则如下(引用C ++ 14最终草案N4140,强调我的):
<强> 10.3节/ 15 强>
使用范围运算符(5.1)进行显式限定可以抑制虚拟调用机制。
<强>§5.2.2/ 1 强>
...如果所选功能是非虚拟的,或者如果是 类成员访问表达式中的 id-expression 是一个 qualified-id, ,该函数被调用。否则,它 调用对象表达式的动态类型中的最终覆盖(10.3);这样的呼叫被称为a 虚拟函数调用。