3

请看这个例子

#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。

很明显,现在的规则已经改变了它原来的含义。这是新规则的缺陷吗?或者,只是为了改变需求而进行的人为设计?

4

0 回答 0