This is because using declaration does not introduce a new member or a new definition. Rather it introduces a set of declaration that can be found by qualified name look up [namespace.udecl]/1:
Each using-declarator in a using-declaration, introduces a set of declarations into the declarative region in which the using-declaration appears. The set of declarations introduced by the using-declarator is found by performing qualified name lookup ([basic.lookup.qual], [class.member.lookup]) for the name in the using-declarator, excluding functions that are hidden as described below.
It only has influence on the entity(ies) found by qualified name lookup. As such, it does not have influence in the definition of the final overrider [class.virtual]/2:
[...] A virtual member function C::vf of a class object S is a final overrider unless the most derived class ([intro.object]) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf.
Which has a different meaning than: the final overrider is the entity designated by the expression D::vf where D is the most derived class of which S is a base class suboject.
And as a consequence, it does not influence if a class is an abstract class [class.abstract]/4:
A class is abstract if it contains or inherits at least one pure virtual function for which the final overrider is pure virtual.
Note 1:
The consequence is that a using directive will result in different behavior for non virtual and virtual functions [expr.call]/3:
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 in the dynamic type of the object expression is called; such a call is referred to as a virtual function call.
Simply:
- non virtual function => function found by qualified name lookup
- virtual function => call the final overrider
So if print
was not virtual:
class A {
public :
void print() {
std::cout << "\n Class A::print()";
}
};
int main() {
B b;
C c;
b.print() // Class B print ()
c.print() // Class A print ()
//Equivalent to:
c.C::print() // Class A::print()
return 0;
}
Note 2:
As some may have noticed in the preceding standard paragraph, it is possible to performe a qualified call of a virtual function to get the non-virtual behavior. So a using declaration of virtual function may be practical (probably a bad practice):
class A {
public :
virtual void print() =0;
};
//Warning arcane: A definition can be provided for pure virtual function
//which is only callable throw qualified name look up. Usualy an attempt
//to call a pure virtual function through qualified name look-up result
//in a link time error (that error message is welcome).
void A::print(){
std::cout << "pure virtual A::print() called!!" << std::endl;
}
int main() {
B b;
C c;
b.print() // Class B print ()
c.print() // Class B print ()
c.C::print() // pure virtual A::print() called!!
//whitout the using declaration this last call would have print "Class B print()"
return 0;
}
Live demo