请看这个例子
#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();
}
GCCbase
在 Clang 提示错误诊断时打印。在当前草案中,相关规则是这样定义的:
[class.virtual#2]
如果在类 B 中声明了一个虚成员函数 F,并且在从 B 派生(直接或间接)的类 D 中,成员函数 G 的声明对应于 ([basic.scope.scope]) F 的声明,忽略尾随的要求子句,然后 G 覆盖 F。
同时,定义两个对应的声明的规则是这样定义的:
[basic.scope#scope-3]
如果两个声明(重新)引入相同的名称,都声明构造函数,或都声明析构函数,则两个声明对应,除非
- [...]
- each 声明一个函数或函数模板,除非
- 两者都声明具有相同参数类型列表的函数,等效 ([temp.over.link]) 尾随要求子句(如果有,除非 [temp.friend] 中指定),并且如果两者都是非静态成员, 相同的 cv-qualifiers (如果有的话) 和 ref-qualifiers (如果两者都有的话), 或者
在此示例中,#1
并且#2
具有相同的参数类型列表,#1
具有一个引用限定符而#2
没有,因此可以忽略后一个限制,因此,对于这两个声明,我们可以说它们是对应的。因此,#2
覆盖#1
声明为虚函数。因此,如果我们遵守当前的草案,我们可以说 GCC 和 Clang 的结果都是错误的。
但是,在c++20标准中,如何确定派生类中声明的声明覆盖基类中声明的虚函数的限制是:
[class.virtual#2]
如果虚成员函数 vf 在类 Base 和 Derived 类中声明,直接或间接从 Base 派生,则具有相同名称、参数类型列表 ([dcl.fct])、cv 限定的成员函数 vf , 和 ref-qualifier (或没有相同)作为 Base::vf 被声明,然后 Derived::vf 覆盖 Base::vf。
很明显,现在的规则已经改变了它原来的含义。这是新规则的缺陷吗?或者,只是为了改变需求而进行的人为设计?