Изменилось ли требование о том, как переопределить виртуальную функцию?
Пожалуйста, посмотрите этот пример
#include <iostream>
class B{
public:
virtual void fun() &{ //#1
std::cout<<"base\n";
}
};
class A:public B{
public:
void fun(){ //#2
std::cout<<"override\n";
}
};
int main(){
A a;
B* ptr = &a;
ptr->fun();
}
Печать GCC
base
в то время как Clang предлагает диагностику ошибки. В текущем черновике соответствующее правило определено следующим образом:
[class.virtual#2]
Если виртуальная функция-член F объявлена в классе B, а в классе D, производном (прямо или косвенно) от B, объявление функции-члена G соответствует ([basic.scope.scope]) объявлению F , игнорируя завершающую требует-положений, то О переопределяет F .
Одновременно правило определения двух соответствующих объявлений определяется следующим образом:
[basic.scope#scope-3]
Два объявления соответствуют, если они (повторно) вводят одно и то же имя, оба объявляют конструкторы или оба объявляют деструкторы, если только
- [...]
- каждый объявляет функцию или шаблон функции, кроме случаев, когда
- обе объявляют функции с одним и тем же списком-тип-параметра, эквивалентными ([temp.over.link]) завершающими предложениями-требованием (если есть, кроме указанных в [temp.friend]), и, если оба являются нестатическими членами , одинаковые cv-квалификаторы (если есть) и ref-qualifier ( если они есть у обоих ), или
В этом примере и имеют один и тот же список-типов-параметров, имеется один квалификатор ref, а его нет, поэтому последнее ограничение можно игнорировать, следовательно, для этих двух объявлений мы могли бы сказать, что они соответствуют. Следовательно,
#2
отменяет
#1
который объявлен как виртуальная функция. Следовательно, мы можем сказать, что и GCC, и Clang имеют неправильный результат, если мы подчиняемся текущему проекту.
Однако в стандарте C ++ 20 ограничение на определение объявления, объявленного в производном классе, переопределяет виртуальную функцию, объявленную в базовом классе:
[class.virtual#2]
Если виртуальная функция-член vf объявлена в классе Base и в классе Derived, производном прямо или косвенно от Base, функция-член vf с тем же именем, список-типов-параметров ([dcl.fct]), cv-qualification , и квалификатор ref (или его отсутствие) объявлен как Base :: vf, тогда Derived :: vf переопределяет Base :: vf.
Очевидно, что действующее правило изменило свой первоначальный смысл. Это недоработка в новом правиле? Или это просто искусственный замысел, заставляющий изменить требование?