Это потому, что при использовании объявления не вводится новый член или новое определение. Вместо этого представляет набор объявлений , которые можно найти по квалифицированному имени. Look up [namespace.udecl] / 1 :
Каждый декларатор использования в объявлении использования, вводит набор объявлений в декларативную область, в которой появляется объявление использования . Набор объявлений, введенных декларатором using, определяется путем выполнения поиска подходящего имени ([basic.lookup.qual], [class.member.lookup]) для имени в деклараторе использования, за исключением функций, которые скрыты, как описано ниже.
Он влияет только на сущность (и), найденную при поиске квалифицированного имени. Таким образом, он не влияет на определение окончательного переопределения [class.virtual] / 2 :
[...] Виртуальная функция-член C :: vf объекта класса S является окончательным переопределением, если только самый производный класс ([intro.object]), в котором S не является подобъектом базового класса (если есть) объявляет или наследует другую функцию-член , которая переопределяет vf.
Имеет значение, отличное от: конечный переопределитель - это сущность, обозначенная выражением D :: vf, где D - наиболее производный класс, из которого S является подобъектом базового класса.
И, как следствие, не влияет, является ли класс абстрактным классом [class.abstract] / 4 :
Класс является абстрактным, если он содержит или наследует хотя бы одну чисто виртуальную функцию, для которой конечный переопределитель является чисто виртуальным.
Примечание 1:
Следствием этого является то, что директива using приведет к разному поведению не виртуальных и виртуальных функций [expr.call] / 3:
Если выбранная функция не виртуальная или если id-выражение в выражении доступа к члену класса является qualid-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:
Как некоторые, возможно, заметили в предыдущем стандартном параграфе, можно выполнить квалифицированный вызов виртуальной функции, чтобы получить не виртуальное поведение. Поэтому объявление использования виртуальной функции может быть практичным (вероятно, плохой практикой):
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;
}
Демонстрационная версия