入れ子になったクラスに対するプライベートに継承された typedef の可視性

StackOverflow https://stackoverflow.com/questions/2421492

質問

次の例では (長くなったことをお詫びします)、別のクラスからプライベートに継承するクラス内でネストされたクラスを使用するときに遭遇したいくつかの予期せぬ動作を分離しようとしました。ネストされていないクラスと比較して、ネストされたクラスには特別なことは何もないという趣旨のステートメントをよく目にしますが、この例では、ネストされたクラス (少なくとも GCC 4.4 によれば) は、クラスの public typedef を参照できることがわかります。終了クラスによってプライベートに継承されるクラス。

typdef がメンバー データと同じではないことは理解していますが、この動作は驚くべきことだと思いましたし、他の多くの人も同様だと思います。したがって、私の質問は 2 つあります。

  1. これは標準的な動作ですか?(理由についての適切な説明は非常に役立ちます)
  2. それがほとんどの最新のコンパイラで動作することを期待できますか (つまり、移植性はどれほど高いでしょうか)?

#include <iostream>

class Base {
  typedef int priv_t;
  priv_t priv;
public:
  typedef int pub_t;
  pub_t pub;
  Base() : priv(0), pub(1) {}
};

class PubDerived : public Base {
public:
  // Not allowed since Base::priv is private
  // void foo() {std::cout << priv << "\n";}

  class Nested {
    // Not allowed since Nested has no access to PubDerived member data
    // void foo() {std::cout << pub << "\n";}

    // Not allowed since typedef Base::priv_t is private
    // void bar() {priv_t x=0; std::cout << x << "\n";}
  };

};

class PrivDerived : private Base {
public:
  // Allowed since Base::pub is public
  void foo() {std::cout << pub << "\n";}

  class Nested {
  public:
    // Works (gcc 4.4 - see below)
    void fred() {pub_t x=0; std::cout << x << "\n";}
  };
};

int main() {

  // Not allowed since typedef Base::priv_t private
  // std::cout << PubDerived::priv_t(0) << "\n";

  // Allowed since typedef Base::pub_t is inaccessible
  std::cout << PubDerived::pub_t(0) << "\n"; // Prints 0

  // Not allowed since typedef Base::pub_t is inaccessible
  //std::cout << PrivDerived::pub_t(0) << "\n";

  // Works (gcc 4.4)
  PrivDerived::Nested o;
  o.fred(); // Prints 0
  return 0;
}
役に立ちましたか?

解決

をはじめ、のI以下の回答でC ++ 98とC ++ 03の間にいくつかの違いを参照してください。しかし、それは私が話している変更は、まだ標準にそれを行っていないので、C ++ 03は、その点でC ++ 98(これを指摘してヨハネスに感謝)から実際に異なっていないことが判明します。どういうわけか私はC ++ 03でそれを見て確信していたが、実際にはそれがありません。しかし、問題は確かに存在しない(ヨハネスコメントでDRリファレンスを参照)、いくつかのコンパイラは、すでに彼らはおそらくその問題の最も合理的な解決を考えるものを実装します。だから、下のテキストではC ++ 03への参照が正しくありません。 、いくつかのコンパイラは、すでに実装しようとしているこの行動、いくつかの仮定のが、非常に可能性の高い将来の仕様への参照としてC ++ 03への参照を解釈してください。

<時間>

C ++ 98とC ++ 03標準間の入れ子になったクラスのアクセス権に大きな変化があったことに注意することが重要です。

C ++ 98のネストされたクラスでは、クラスを囲むのメンバーに対して特別なアクセス権がありませんでした。それだけで囲まれたクラスのスコープで宣言され、基本的には完全に独立したクラスでした。それができる唯一のアクセスの囲むクラスの公開のメンバーます。

C ++ 03ネストされたクラスでは、包含するクラスのメンバーのよう囲むクラスののメンバーへのアクセス権を与えられました。より正確には、ネストされたクラスは、の包含するクラスの静的メンバ関数と同じアクセス権をの与えられました。即ち今、ネストされたクラスは、のプライベートのものも含めて、包含するクラスののいずれかののメンバーにアクセスすることができます。

このような理由から、あなたは彼らが新しい仕様を実装するときに応じて、異なるコンパイラと同じコンパイラのバージョンの違いを観察することがあります。

もちろん、ネストされたクラスのオブジェクトを囲むクラスのいずれかの特定のオブジェクトにどのような方法で結ばれていないことを覚えておく必要があります。限り実際のオブジェクトが懸念しているとして、これらは、2つの独立したクラスです。ネストされたクラスから包含するクラスの非静的データメンバやメソッドにアクセスするためには、囲むクラスの特定のオブジェクトを持っている必要があります。それは、囲むクラスのための具体的なthisポインタを持っていないので、非アクセスすることはできません。言い換えれば、再び、ネストされたクラスは、実際と同じくらい、の静的メンバ関数の囲むクラスのように振る舞いますあなたがアクセスし、それを囲むクラスの特定のオブジェクトを与えるために努力をしない限り、包含するクラスの-staticメンバー、。それがなければ、ネストされたクラスは、唯一のtypedef名、列挙型、および外側のクラスの静的メンバにアクセスすることができます。

のように見えるかもしれないC ++ 98とC ++ 03との間の差を示す簡単な例は、以下の

class E {
  enum Foo { A };
public:
  enum Bar { B };

  class I {
    Foo i; // OK in C++03, error in C++98
    Bar j; // OK in C++03, OK in C++98
  };
};

この変更は、あなたのPrivDerived::Nested::fred機能をコンパイルすることができます正確に何です。それは知識をひけらかすC ++ 98コンパイラでコンパイルを渡しません。

他のヒント

短い答え:ネストされたクラスは、C ++ 0xの中に含むクラスのプライベートメンバへのアクセスではなくC ++ 1998とC ++ 2003を持っています。以前の動作が不良と考えられているので、C ++ 98とC ++ 2003コンパイラは、C ++ 0xの動作をサポートするためには、の法的しかしである。

でC ++ 98と2003年の標準セクション11.8.1は述べています:

  

ネストされたクラスのメンバーはありません持っています   のメンバーへの特別なアクセス   、またクラスにクラスを囲みますか、   友情を付与した機能   包含するクラスへ。いつもの   アクセスルール(11節)がしなければなりません   従いました。囲んのメンバー   クラスはへの特別なアクセス権を持っていません   入れ子になったクラスのメンバー。いつもの   アクセスルール(11節)がしなければなりません   従います。

C ++ 0xのセクション11.8.1氏は述べています:

  

ネストされたクラスは、次のような部材であり   任意のと同じアクセス権を持っています   他のメンバー。のメンバー   囲むクラスは、特別なアクセス権を持っていません   入れ子になったクラスのメンバーに。インクルード   通常のアクセス規則(第11条)条   従わなけれます。

コア言語不具合レポート45回ののショー必須ではないが、それは、また新しい動作をサポートするために、非C ++ 0xのコンパイラのために合法的であるように、元の動作は、欠陥とみなされたこと。

標準に従って:

9.2 クラスメンバー

1 ...]クラスのメンバーは、データメンバー、メンバー関数(9.3)、ネストされたタイプ、および列挙者です。データ メンバーとメンバー関数は静的または非静的です。9.4.NESTEDタイプは、クラスで定義されているクラス(9.1、9.7)および列挙(7.2)であり、Typedef宣言(7.1.3)を使用してメンバーとして宣言された任意のタイプです。

質問に答えるには:

  1. これは標準的な動作ですか?(なぜ非常に役立つのかについてのまともな説明)

いいえ。少なくとも typedefアクセスできない。ただし、次の点に注意してください。

class Nested {
    // Not allowed since Nested has no access to PubDerived member data
     void foo() {std::cout << pub << "\n";}

問題がある。ネストされたクラスにはインスタンスがありません。 PubDerived 一緒に仕事をすることも、 pub ある static メンバーオブジェクト。

  1. ほとんどの最新のコンパイラで動作すると期待できます(つまり、ポータブルです)?

はい。ただし、標準への準拠についてはドキュメントを確認してください。そして、いつも:Comeau などのいくつかのコンパイラを厳密モードで試してください。

私は、ISO / IEC 14882からすべての関連条項を組み立てるために自分のベストをやった:1997

セクション9.7:

  

別内に定義されたクラスは、ネストされたクラスと呼ばれています。入れ子になったクラスの名前は、その外側のクラスに対してローカルです。ネストされたクラスは、その外側のクラスのスコープ内にあります。明示的なポインタ、参照、およびオブジェクト名を使用する場合を除き、ネストされたクラスの宣言は唯一の名前の、静的メンバ、および外側のクラスからの列挙子を入力を使用することができます。

11.2.1(かなり明白でなければなりません):

  

[...]クラスはプライベートアクセス指定子を使用して、別のクラスの基本クラスであると宣言されている場合は、基底クラスのpublicおよびprotectedメンバーは、派生クラスのプライベートメンバーとしてアクセス可能です。

9.9ネストされたタイプ名:

  

型の名前は他の名前とまったく同じスコープ規則に従う。

次に、11.8でます:

  

ネストされたクラスのメンバーは、囲むクラスのメンバーに、また外側のクラスに友情を付与しているクラスや関数への特別なアクセス権を持っていません。通常のアクセスルール(11)が従わなければなりません。包含するクラスのメンバーは、入れ子になったクラスのメンバーへの特別なアクセス権を持っていません。通常のアクセスルール(11)が従わなければならない。

私はあなたが経験している行動は、非標準であると結論付けているから。ネストされたクラスは、ベースのプライベートメンバへの「特別のアクセスを持つべきではありません。

しかし、最高の標準をサポートしているようだコモC ++は、GCCと同じ動作をしますが(fredすることができます、とbarを禁止「エラー:タイプ 『ベース:: priv_t』(4行目で宣言)はアクセスできません」)ます。

これはあなたの質問の答えにはなりませんが、私の読んだところによると、 C++ FAQ Lite 24.6 あなたがやろうとしていることは許可されていません。なぜ gcc がそれを許可しているのかわかりません。他のコンパイラでも試してみましたか?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top