这是因为 using 声明不会引入新成员或新定义。相反,它引入了一组声明,可以通过限定名称查找[namespace.udecl]/1:
using-declaration 中的每个 using-declarator,将一组声明引入到 using-declaration 出现的声明性区域中。通过对 using-declarator 中的名称执行限定名称查找 ([basic.lookup.qual], [class.member.lookup]) 来找到 using-declarator 引入的声明集,不包括如所述隐藏的函数下面。
它只对通过限定名称查找找到的实体有影响。因此,它对 final overrider [class.virtual]/2:
的定义没有影响
[...] 类对象 S 的虚成员函数 C::vf 是最终覆盖器,除非其中 S 是基类子对象(如果有)的最派生类 ([intro.object]) 声明或继承另一个覆盖 vf 的成员函数。
其含义与以下内容不同:最终覆盖者是由表达式 D::vf 指定的实体,其中 D 是派生度最高的类,而 S 是其基类子对象。
因此,它不会影响一个类是否为抽象类[class.abstract]/4:
一个类是抽象的,如果它包含或继承了至少一个最终覆盖器是纯虚拟的纯虚函数。
注1:
结果是 using 指令将导致非虚函数和虚函数的不同行为 [expr.call]/3:
如果选择的函数是非虚函数,或者类成员访问表达式中的 id-expression 是限定 id,则调用该函数。
否则,在对象表达式的动态类型中调用它的最终覆盖器;这种调用称为虚函数调用。
简单地说:
- 非虚函数 => 通过限定名查找找到的函数
- 虚函数 => 调用最终覆盖器
所以如果print 不是虚拟的:
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;
}
注2:
正如一些人在前面的标准段落中可能已经注意到的那样,可以执行一个合格的虚函数调用来获得非虚行为。所以虚函数的 using 声明可能是实用的(可能是一种不好的做法):
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