Have a look at the following code:
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;
}
Per my understanding, the B::f()
in C
should hide the A::f()
which is brought to C
by using-declaration; if so, then why does c.C::f()
still call A::f()
?
If c.C::f()
calls A::f()
, that should mean that in the scope of C
, f()
should be always refer to A::f()
, this is the function of the using-declaration. Then why in the C::test()
, call to f()
is still evaluated to B::f()
?
Very nice question, a complicated case of name lookup.
Basically, when the name f
is looked up in the scope of C
, it always finds A::f
due to the using-declaration. So all the calls c.f()
, c.C::f()
, and f()
in C::test()
, resolve the name f
to A::f
.
Next comes virtual dispatch. If a virtual function is called by an unqualified name, dynamic dispatch happens and the final overrider is called. This covers c.f()
and the f()
call in C::test()
, since these are unqualified.
The call c.C::f()
uses a qualified name for f
, which suppresses dynamic dispatch and the function to which the name resolved is called directly. Since that function is A::f
(thanks to the using-declaration), A::f
is called non-virtually. The relevant rules follow (quoting C++14 final draft N4140, emphasis mine):
§10.3/15
Explicit qualification with the scope operator (5.1) suppresses the virtual call mechanism.
§5.2.2/1
... If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider (10.3) in the dynamic type of the object expression is called; such a call is referred to as a virtual function call.