13

このコード例は、直感的でない言語機能を説明しています。

class A {
public:
  A() {}
};

class B: private A
{
public:
  B() {}
};

class C: public B
{
public:
  C() {}
  void ProcessA(A* a) {
  }
};

int main() {
  C c;
}

このコードを Mac 上の Apple LLVM バージョン 4.2 でコンパイルすると、

test.cc:16: error: ‘class A’ is inaccessible
test.cc:16: error: within this context

に置き換えるvoid ProcessA(A* a)void ProcessA(::A* a)ビルドされますが、ここで絶対クラス名を使用する必要がある理由がわかりません。特定の種類のエラーを回避するためにある言語機能ですか、それとも> >他のテンプレートでパラメーター化されたテンプレートで山かっこ ( ) の間にスペースを入れる必要があるような暗い C++ 文法コーナーにすぎませんか。ありがとう!

4

1 に答える 1

11

ヒューマントーク

ここで何が起こっているかを説明することから始めましょう。すでに知っている場合はご容赦ください。ただし、フォローアップに必要なコンテキストが作成されます。

コンパイラは、修飾Aされていないものをに解決し::C::Aます (ソース レベルで自分で変更を加えた場合、結果は同じになります)。にアクセスできないため::C::A、エラー メッセージが出力されます。

::C::Aアクセスできないことをコンパイラが検出し、への参照をフォールバックとしてAへの参照と見なす必要があることを提案しています。::Aただし、::C::A::Aは簡単に 2 つのまったく異なるものになる可能性があります。

ここで何をすべきかを自動的に推測することは、バグや髪を引っ張る¹またはその両方を導入する傾向があるだけでなく、C++ の精神に完全に反します。

標準語

この動作が C++11 標準に直接準拠し、設計どおりであることを確認します。

§9/2 は次のように述べています。

クラス名は、クラス名が表示された直後に宣言されているスコープに挿入されます。class-nameは、クラス自体のスコープにも挿入されます。これは、 注入されたクラス名として知られています。

これは、 class のスコープ内にCA注入されたクラス名があることを意味します。

§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()たため失敗すると予想されますが、代わりに の型が背後で変更され、「メソッドがありません」というエラーが発生します。は?!?もちろん、それは起こりうる最悪の事態とはほど遠いものです。fooafoo

于 2013-03-11T15:34:23.260 に答える