この例を参照してください
#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();
}
base
Clangがエラー診断を促している間にGCCが印刷します。現在のドラフトでは、関連するルールは[class.virtual#2]のように定義されています。
仮想メンバー関数 F がクラス B で宣言され、B から (直接的または間接的に) 派生したクラス D で宣言されている場合、メンバー関数 G の宣言は ([basic.scope.scope]) F の宣言に対応します。、後続の requires-clauses を無視すると、 G が F をオーバーライドします。
同時に、対応する 2 つの宣言を定義するための規則は次のように定義されます。
[basic.scope#scope-3]
2 つの宣言が同じ名前を (再) 導入する場合、両方がコンストラクターを宣言する場合、または両方がデストラクタを宣言する場合は対応します。
- [...]
- それぞれが関数または関数テンプレートを宣言します。
- 両方とも、同じパラメータ型リスト、同等の ([temp.over.link]) 後続の requires-clauses ([temp.friend] で指定されている場合を除く) を持つ関数を宣言し、両方が非静的メンバである場合、同じ cv-qualifiers (存在する場合) および ref-qualifier (両方に 1 つある場合)、または
この例で#1
は#2
、同じ parameter-type-list を#1
持ち、1 つの ref-qualifier を持ち#2
ますが、持っていません。したがって、後者の制限は無視できます。したがって、これら 2 つの宣言については、それらが対応していると言えます。したがって、仮想関数として宣言されている#2
オーバーライド。#1
したがって、現在のドラフトに従うと、GCC と Clang の両方が間違った結果になると言えます。
ただし、c++20 標準では、派生クラスで宣言された宣言が基底クラスで宣言された仮想関数をオーバーライドするかどうかを判断する方法の制限は次のとおりです。
[class.virtual#2]
仮想メンバー関数 vf がクラス Base およびクラス Derived で宣言され、Base から直接的または間接的に派生した場合、同じ名前、parameter-type-list ([dcl.fct])、cv-qualificationを持つメンバー関数 vf 、および Base :: vf として ref-qualifier (または同じものがない)が宣言されている場合、Derived :: vf は Base :: vf をオーバーライドします。
現在のルールが元の意味を変更したことは明らかです。新しいルールの欠陥ですか?それとも、要件を変更するための人為的な設計ですか?