25

看看下面的代码:

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()inC应该隐藏using-declarationA::f()带来的内容;C如果是这样,那为什么c.C::f()还要打电话A::f()

如果c.C::f()调用A::f(),那应该意味着在 的范围内Cf()应该总是引用A::f(),这是 using-declaration 的功能。那么为什么在C::test(), call tof()仍然被评估到B::f()

4

1 回答 1

29

非常好的问题,一个复杂的名称查找案例。

基本上,当f在 的范围内查找名称时,由于使用声明C,它总是会找到。A::f所以所有的调用c.f(),c.C::f()f()in都C::test()将名字解析fA::f.

接下来是虚拟调度。如果一个虚函数被一个不合格的名字调用,动态调度就会发生并且最终的覆盖器被调用。这包括c.f()f()调用C::test(),因为这些是不合格的。

该调用c.C::f()使用限定名称 for f,它禁止动态调度和直接调用解析名称的函数。由于该函数是A::f(由于使用声明),A::f因此被非虚拟调用。相关规则如下(引用 C++14 final draft N4140,强调我的):

§10.3/15

范围运算符(5.1)的显式限定抑制了虚拟调用机制。

§5.2.2/1

...如果所选函数是非虚拟的,或者如果类成员访问表达式中的 id-expression限定 id,则调用该函数。否则,它在对象表达式的动态类型中的最终覆盖器(10.3)被调用;这种调用称为 虚函数调用。

于 2015-02-11T08:14:53.773 回答