使用法は、継承された仮想関数を隠すことになっていますか?
-
10-10-2019 - |
質問
struct level0
{
virtual void foo() = 0;
};
struct level1 : level0
{
virtual void foo() { cout <<" level1 " << endl; }
};
struct level2 : level1
{
virtual void foo() { cout <<" level2 " << endl; }
};
struct level3 : level2
{
using level1::foo;
};
int main()
{
level1* l1 = new level3;
l1->foo();
level3 l3;
l3.foo();
return 0;
}
GCCを使用した上記のコードは与えます
level2
level1
しかし、ICCでは与えます
level2
level2
どちらが正しいのか、それとも標準によって未定義ですか?
編集:これは確かにバグがあることを証明しています、次の主な機能を考慮してください
int main()
{
level3 l3;
l3.foo(); // This prints level1
level3* pl3 = &l3;
pl3->foo(); // This prints level2
level3& rl3 = l3;
rl3.foo(); // This prints level1
level3& rpl3 = *pl3;
rpl3.foo(); // This prints level2
return 0;
}
したがって、同じオブジェクトが直接異なる結果を生成し、同じタイプのポインターを介して使用すると、異なる結果が生成されます!!!
解決
標準セクション10.3p2の例では、宣言を使用することは明らかになります いいえ 仮想関数をオーバーライドします。
これは 既知のG ++バグ.
ご存知のように、動的タイプがわかっている場合ではなく、参照またはポインターを介してメンバー関数を呼び出すとき、バグは発生しません。
他のヒント
using level1::foo;
紹介します foo
機能します level3
を参照するクラス level1::foo
.
メンバーの宣言として使用される使用法では、ネストされた名前の特定は、定義されているクラスの基本クラスに名前を付けます。このような使用法は、メンバー名ルックアップによって見つかった宣言のセットを紹介します。
しかし、それ以来 level1::foo
仮想です、私はそれを呼ぶことによってあなたは電話するべきだと思います level2::foo
, 、したがって、ICCは正しいはずです。
とにかくよくわかりません。
もちろん、レベル1レベル1を取得する方法は次のとおりです。
struct level3 : level2
{
virtual void foo() { level1::foo(); }
};
「使用」ディレクティブは、レベル3を持っていてFooを呼び出している場合、レベル1バージョンを呼び出す必要があることをコンパイラに通知しているようですが、これをV-Tableに上書きしていません。
GCCは矛盾のために間違っているように見えますが、標準が何を示しているかわからないため、ICCについてはわかりません。