2

test.cxxに次のようなものがあるとします(そして、意図的に1でオブジェクトスライスを実行します)。

class A {
};

class B : public A {
  // prevent copy construction and assignment
  B(const B& other);
  B& operator=(const B& other);
public:
  explicit B(){}
};

class C {
  A m_a;
public:
  explicit C() : m_a( B() ) {} // 1
};

1の場合、クラスAのコピーコンストラクター(ここではコンパイラーによって生成され、パブリック)を呼び出す必要があるため、これが機能することを期待しています。このコードは最近のコンパイラー(g++-4.4とIntel11.0を試しました)でも正常にコンパイルされますが、古いコンパイラー(g++-4.2やg++-4.0など)は、プライベートと宣言したBのコピーコンストラクターを呼び出そうとします。その結果:

test.cxx:コンストラクター内'C :: C()':
test.cxx:7:エラー:'B :: B(const B&)'はプライベートです
test.cxx:16:エラー:このコンテキスト内

さて、私のビルドシステムで、コンパイラが上記のコードをサポートしているかどうかを確認したいと思います。しかし、質問は、この標準に準拠したコードですか?そして、そのようなテストの固有名は何でしょうか?

編集:申し訳ありませんが、Intelコンパイラバージョン10.1と11.0はどちらも次の問題を発行します:警告#734:削除されたコピーに必要な "B :: B(const B&)"(6行目で宣言)にアクセスできません

4

5 に答える 5

4

この場合、私はあえてコモーに反対します。実際、次のコードは期待どおりにコンパイルできません。これは、右辺値をconst参照にバインドするには、アクセス可能なコピーコンストラクターが必要なためです。

class A {
};

class B : public A {
  B(const B& other);
  B& operator=(const B& other);
public:
  explicit B(){}
};

int main()
{
    A const & a = B();
}

8.5.3 / 2によると、「[...]引数の受け渡し(5.2.2)と関数値の戻り値(6.6.3)は初期化です」。そのため、コードは不正な形式である必要があります。

編集:私はまだコードがC++03に従って不正な形式であると固く信じています。ただし、C ++ 0xの作業ドラフトの関連セクションを読んだばかりで、コピーコンストラクターが使用可能である必要がなくなったようです。おそらくそれが、gcc-4.2からgcc-4.3に移行したときにコードのコンパイルが開始された理由です。

編集:明確にするために、アクセス可能でなければならない理由は、の最初のパラメーターへB::B(const B &)のバインドによるものです(もちろん、初期化時に呼び出されます)。B()A::A(const A &)m_a

編集: C++03とC++0xの違いに関して、litbは、関連する欠陥レポートを見つけるのに十分親切でした。

于 2009-08-20T08:31:15.517 に答える
1

古いg++コンパイラは、使用可能なコピーコンストラクタがない場合、参照によって一時オブジェクトを渡すことができないようです。

class A { 
  A(const A& other);
  A& operator=(const A& other);
public:
  explicit A(){}
};
void f( const A& a ) {}
int main() {
  A a;
  f( a );    // fine
  f( A() );  // fails
}
于 2009-08-20T09:22:52.543 に答える
1

C ++ 0xでは標準に準拠したコードだと思いますが、C++03ではそうではありません。

テストには「右辺値からのコピー構築」のような名前を付けます。

これはエラーとして報告されましたが、gccの人々はここでそれが正しい動作であると主張し、標準への参照を提供します。

[dcl.init.ref] / 5、箇条書き2、サブ箇条書き1

T2がクラス型で、「cv1T1」が「cv2T2」と参照互換である右辺値の初期化式の場合、参照は次のいずれかの方法でバインドされます(選択は実装で定義されます)
。右辺値(3.10を参照)で表されるオブジェクトまたはそのオブジェクト内のサブオブジェクトにバインドされます。
-タイプ"cv1T2" [sic]の一時オブジェクトが作成され、コンストラクターが呼び出されて、右辺値オブジェクト全体が一時オブジェクトにコピーされます。参照は、一時または一時内のサブオブジェクトにバインドされます。

コピーを作成するために使用されるコンストラクターは、コピーが実際に行われたかどうかに関係なく呼び出し可能でなければなりません

C ++ 0x標準はあいまいさを取り除き、参照は常に右辺値で表されるオブジェクトにバインドされ、コンストラクターにアクセスできる必要はありません。

于 2009-08-20T09:45:50.767 に答える
0

標準の特定のセクションを指摘することはできませんが、それは確かに私には問題ないように見え、ComeauC++とあなたが言及したコンパイラでコンパイルします。そのようなテストの名前としては、「コンパイラの互換性」は何よりも良いと思います。

于 2009-08-20T08:14:58.453 に答える
0

有効です。B()を呼び出すと、Bオブジェクトが作成されます。Bのデフォルトのコンパイラ生成パブリックコンストラクタが呼び出されます。

Cオブジェクトが作成されると、Aオブジェクトが作成されます。これは値であるため、静的型付けが考慮され、Aから派生し、Aオブジェクトのコピー構築に使用されるすべてのオブジェクトでスライスが発生します。クラスAには、コンパイラーによって提供されるパブリックコピーコンストラクターがあります。コンパイラーは、BオブジェクトもタイプAであることを確認します。コンパイラーは、Aオブジェクトのコピー構築のみに関与し、これを実行できることを認識しているため、Aコピーコンストラクターを使用してBオブジェクト内のAオブジェクトをCにコピーします。 :m_aオブジェクト。つまり、静的型付けによるスライス。

これらのオブジェクトのメモリを確認することは有益です。「I'maA」に初期化されたAのchar*データメンバーと「I'maB」に初期化されたBのchar*データメンバーを配置し、デバッガーをステップ実行してオブジェクトをモニターします。あなたはそれらの文字列を十分に簡単に見るはずです。

于 2009-08-20T11:22:13.987 に答える