ヒューマントーク
ここで何が起こっているかを説明することから始めましょう。すでに知っている場合はご容赦ください。ただし、フォローアップに必要なコンテキストが作成されます。
コンパイラは、修飾A
されていないものをに解決し::C::A
ます (ソース レベルで自分で変更を加えた場合、結果は同じになります)。にアクセスできないため::C::A
、エラー メッセージが出力されます。
::C::A
アクセスできないことをコンパイラが検出し、への参照をフォールバックとしてA
への参照と見なす必要があることを提案しています。::A
ただし、::C::A
と::A
は簡単に 2 つのまったく異なるものになる可能性があります。
ここで何をすべきかを自動的に推測することは、バグや髪を引っ張る¹またはその両方を導入する傾向があるだけでなく、C++ の精神に完全に反します。
標準語
この動作が C++11 標準に直接準拠し、設計どおりであることを確認します。
§9/2 は次のように述べています。
クラス名は、クラス名が表示された直後に宣言されているスコープに挿入されます。class-nameは、クラス自体のスコープにも挿入されます。これは、
注入されたクラス名として知られています。
これは、 class のスコープ内にC
、A
注入されたクラス名があることを意味します。
§3.4/3 は、注入されたクラス名が名前検索の候補であると述べています。
クラスの注入されたクラス名も、名前の隠蔽と検索のために、そのクラスのメンバーと見なされます。
§3.4/1 は、ベースにアクセスできA
ないことが、注入されたクラス名の A
考慮を妨げないことを明確にしています。
アクセス ルールは、名前の検索と関数のオーバーロードの解決 (該当する場合) が成功した場合にのみ考慮されます。
§11.1/5 は、議論中の正確な状況を直接説明しています。
[注: 派生クラスでは、基本クラス名のルックアップは、それが宣言されたスコープ内の基本クラスの名前ではなく、注入されたクラス名を見つけます。注入されたクラス名は、それが宣言されたスコープ内の基本クラスの名前よりもアクセスしにくい場合があります。—終わりのメモ]
標準では、次の例も提供されています。これは、あなたのものと同等です。
class A { };
class B : private A { };
class C : public B {
A *p; // error: injected-class-name A is inaccessible
::A *q; // OK
};
A
¹が最初はpublic
ベースで、後でprivate
リファクタリング中になるとどうなるか想像してみてください。::A
また、と::C::A
は無関係であることを想像してください。(以前は機能していた)のような呼び出しはアクセスできなくなっa->foo()
たため失敗すると予想されますが、代わりに の型が背後で変更され、「メソッドがありません」というエラーが発生します。は?!?もちろん、それは起こりうる最悪の事態とはほど遠いものです。foo
a
foo