3

次のコードを検討してください。

class A
{
    friend class B;
    friend class C;
};

class B: virtual private A
{
};

class C: private B
{
};

int main()
{
    C x; //OK default constructor generated by compiler
    C y = x; //compiler error: copy-constructor unavailable in C
    y = x; //compiler error: assignment operator unavailable in C 
}

MSVC9.0 (Visual Studio 2008 の C++ コンパイラ) は既定のコンストラクターを生成しますが、C は A の仲間ですが、C のコピーおよび代入演算子を生成できません。これは予想される動作ですか、それとも Microsoft のバグですか? 後者の場合だと思います。私が正しければ、この問題が議論されている記事/フォーラム/...、またはマイクロソフトがこのバグに対応している場所を誰かが指摘できます。前もって感謝します。

PS ちなみに、両方のプライベート継承が保護に変更された場合、すべてが機能します

PPS 上記のコードが合法か違法かの証明が必要です。私が理解しているように、仮想プライベートベースを持つクラスは派生できないことが実際に意図されていました。しかし、彼らは友達の部分を見逃しているようです. だから...これが私の最初の報奨金です:)

4

3 に答える 3

2

私が標準を解釈する方法では、サンプル コードは整形式です。(そして、そうです、friend宣言は@Steve Townsendが引用したものとは大きな違いがあります。)

private11.2p1: クラスがアクセス指定子を使用して別のクラスの基本クラスとして宣言されている場合、基本クラスのメンバーpublicと派生クラスのメンバーprotectedとしてアクセスできます。private

11.2p4:クラスNで名前が付けられている場合、メンバーmにアクセスできます。

  • Nのメンバーとしてのmが public である、または
  • Nのメンバーとしてのmがプライベートであり、参照がクラスNのメンバーまたはフレンドで発生する、または
  • Nのメンバーとしてのmは保護されており、参照はクラスNのメンバーまたはフレンド、またはNから派生したクラスPのメンバーまたはフレンドで発生します。ここで、 Pのメンバーとしてのmは非公開または保護されています。
  • 参照点でアクセス可能なNの基本クラスBが存在し、クラスBで名前が付けられたときにmにアクセスできます。

11.4p1: クラスのフレンドは、クラスのメンバーではないが、クラスのプライベートおよび保護されたメンバー名の使用が許可されている関数またはクラスです。

第 11 条 (メンバーのアクセス制御) には、あるクラスのフレンドが、そのクラスとフレンドになったクラスよりもアクセス許可が少ないことを意味する記述はありません。「アクセス可能」は、特定のクラスのコンテキストでのみ定義されることに注意してください。一般に、メンバーまたは基底クラスが「アクセス可能」または「アクセス不可」であると話すことがありますが、「すべてのコンテキストでアクセス可能」または「すべてのクラスでアクセス可能」であるかについて話す方が正確です (この場合のように)。のみpublicが使用される場合)。

次に、自動定義メソッドでのアクセス制御のチェックについて説明する部分です。

12.1p7: クラスの暗黙的に宣言されたデフォルト コンストラクターは、そのクラス型 (1.8) のオブジェクトを作成するために使用されるときに暗黙的に定義されます。暗黙的に定義された既定のコンストラクターは、空の mem-initializer-list (12.6.2) と空の関数本体を使用して、そのクラスのユーザー作成の既定のコンストラクターによって実行されるクラスの初期化のセットを実行します。そのユーザー作成のデフォルト コンストラクターの形式が正しくない場合、プログラムは形式が正しくありません。

12.6.2p6: 仮想基本クラスを表すすべてのサブオブジェクトは、最も派生したクラス (1.8) のコンストラクターによって初期化されます。最も派生したクラスのコンストラクターが仮想基本クラスのmem-initializerを指定しない場合、仮想基本クラス サブオブジェクトを初期化するために の既定のコンストラクターが呼び出されますV。アクセス可能な既定のコンストラクターがないV場合、初期化の形式が正しくありません。V

12.4p5: 暗黙的に宣言されたデストラクタは、そのクラス型 (3.7) のオブジェクトを破棄するために使用されるときに暗黙的に定義されます。デストラクタが暗黙的に定義されているクラスに次のものがある場合、プログラムは不正な形式です。

  • アクセスできないデストラクタを持つクラス型 (またはその配列) の非静的データ メンバー、または
  • アクセスできないデストラクタを持つ基本クラス。

12.8p7: 暗黙的に宣言されたコピー コンストラクターは、そのクラス型またはそのクラス型から派生したクラス型のオブジェクトのコピーからそのクラス型のオブジェクトを初期化するために使用される場合、暗黙的に定義されます。[注: コピー コンストラクターは、実装でその使用が省略されていても暗黙的に定義されます (12.2)。

  • アクセスできないかあいまいなコピー コンストラクターを持つクラス型 (またはその配列) の非静的データ メンバー、または
  • アクセスできないかあいまいなコピー コンストラクターを持つ基本クラス。

12.8p12: コピー代入演算子が暗黙的に定義されているクラスに次のものがある場合、プログラムは不正な形式です。

  • 型の非静的データ メンバーconst、または
  • 参照型の非静的データ メンバー、または
  • アクセスできないコピー代入演算子を持つクラス型 (またはその配列) の非静的データ メンバー、または
  • アクセスできないコピー代入演算子を持つ基本クラス。

「アクセス不可」または「アクセス可能」に言及するこれらすべての要件は、何らかのクラスのコンテキストで解釈する必要があり、意味のある唯一のクラスは、メンバー関数が暗黙的に定義されているクラスです。

元の例では、デフォルトのコンストラクタ、デストラクタ、コピー コンストラクタ、およびコピー代入演算子class Aが暗黙的に含まれています。public11.2p4 では、class Cは のフレンドであるためclass A、クラス で名前を付けると、これらすべてのメンバーにアクセスできますC。したがって、 のこれらのメンバーに対するアクセス チェックによって、の既定のコンストラクタ、デストラクタ、コピー コンストラクタ、またはコピー代入演算子class Aの暗黙的な定義が不適切な形式になることはありません。class C

于 2010-10-12T19:48:14.050 に答える
1

コードは、Comeau Online、およびMinGW g++4.4.1で正常にコンパイルされます。

私はそれを単なる「権威の議論」に言及しています。

標準から、POVアクセスは仮想継承と直交しています。仮想継承の唯一の問題は、仮想継承からのクラスを継承チェーンのさらに上流に(または「あたかも」)初期化するのが最も派生したクラスであるということです。あなたの場合、最も派生したクラスはそれを行うために必要なアクセス権を持っているので、コードはコンパイルする必要があります。

MSVCには、仮想継承に関する他の問題もいくつかあります。

あ、はい、

  • コードは有効であり、
  • これはMSVCコンパイラのバグです。

参考までに、そのバグはMSVC10.0にまだ存在しています。マイクロソフトによって確認された、同様のバグに関するバグレポートを見つけました。しかし、ざっとグーグルするだけで、この特定のバグを見つけることができませんでした。

于 2010-10-12T17:41:43.037 に答える
1

C++ SWG のthisに従って、ベースを持つクラスをvirtual private派生させるべきではありません。コンパイラは正しいことを (ある程度までは) 行っています。問題は C からの A の可視性に関するものではなく、C をインスタンス化することをまったく許可してはならないということです。これは、バグが他の行ではなく最初の (デフォルト) 構造にあることを意味します。

  1. プライベート仮想基底クラスを持つクラスを派生させることはできますか?

セクション: 11.2 [class.access.base]
ステータス: NAD 提出者: Jason Merrill 日付: 不明

class Foo { public: Foo() {}  ~Foo() {} };
class A : virtual private Foo { public: A() {}  ~A() {} };
class Bar : public A { public: Bar() {}  ~Bar() {} }; 

~Bar() は ~Foo() を呼び出しますが、これはアクセス違反のため形式が正しくありません。(Bar のコンストラクターは Foo のコンストラクターを呼び出す必要があるため、同じ問題があります。) コンパイラー間で意見の相違があるようです。Sun、IBM、および g++ はテストケースを拒否し、EDG および HP はそれを受け入れます。おそらく、このケースは草案の注記によって明確にする必要があります。つまり、仮想プライベートベースを持つクラスは派生できないようです。

根拠: これは意図されたものです。

ところで、Visual C++ v10 コンパイラの動作は、質問に記載されているものと同じです。invirtualの継承から削除すると、問題が修正されます。 AB

于 2010-10-10T12:59:33.820 に答える