2

私は次のコードを持っています:

class Foo;
class Bar;

class Bar {
public:
    Bar() {
    }

    Bar(Foo &foo) {
    }
};

class Foo {
public:
    Foo() {
    }
    Foo(Foo &foo) {
    }
    Foo(const Bar &bar) {
    }
};

Bar operator >> (const Bar &left, const Bar &right) { return Bar(); }

Foo a;
Foo b;
Foo c = a >> b;

Visual Studio 10では、上記のコードは正常にコンパイルされます。コンパイラは、Barからインスタンス化できることを認識しFoo&ます。したがって、適切なを呼び出し、operator >>次にインスタンスを返しBar、コンストラクタFoo(const Bar &)を適切に呼び出します。

ただし、GCC4.5は上記のコードをコンパイルしません。次のエラーが出力されます。

error: no matching function for call to 'Foo::Foo(Foo)'
note: candidates are: Foo::Foo(const Bar&)
note:                 Foo::Foo(Foo&)
note:                 Foo::Foo()

言語標準によると、なぜ上記が発生し、どのコンパイラが正しいのですか?

編集:

存在するのに、なぜC ++Fooはの結果として一時オブジェクトを作成するのですか?c = a >> bFoo(const Bar &)

4

2 に答える 2

4

これは、オーバーロードや引数の検索とは関係ありません。を定義することで、右辺値から初期化する必要があるFoo(Foo&)デフォルトのコピー コンストラクターの生成を無効にしました。Foo(const Foo&) シグネチャを持つ ctor を追加すると、コードが正常に実行されます。VS10がこれをコンパイルする理由はわかりませんが、なぜそれが起こらないのかを指定する句を調べてみます.Foo(const Foo&)c

§12.8 (1) は、クラス X の非テンプレート コンストラクターが、その最初のパラメーターが X&、const X&、volatile X&、または const volatile X& 型であり、他のパラメーターまたはすべてのパラメーターがない場合、コピー コンストラクターであることを指定します。デフォルト値があります。

§12.8(5) は、X のコピー コンストラクターを (上記の形式のいずれかで) 定義しない場合、コンパイラーは X(const X&) の形式でコピー コンストラクターを定義すると述べています。

したがって、Foo(Foo&) を定義するとコピー コンストラクターが定義されるため、コンパイラーは Foo(const Foo&) を暗黙的に定義できなくなります。

于 2011-06-15T13:10:36.600 に答える
2

これはうまくいくはずです:

Foo c(a >> b);

この初期化構文:

Foo c = a >> b;

次のように解釈されます

Foo c(Foo(a >> b));

コピー初期化であり、 を受け入れるアクセス可能なコピー コンストラクターが必要const Foo&です。


標準のセクション[dcl.init]から (C++0x の文言):

初期化の形式 (括弧または = を使用) は一般的に重要ではありませんが、初期化子または初期化されるエンティティにクラス型がある場合は問題になります。下記参照。初期化されるエンティティにクラス型がない場合、括弧で囲まれた初期化子の式リストは単一の式になります。

フォームで発生する初期化

   T  x  =  a;

引数の受け渡し、関数の戻り、例外のスロー (15.1)、例外の処理 (15.3)、および集約メンバーの初期化 (8.5.1) と同様に、copy-initializationと呼ばれます。[ 注: コピーの初期化は、移動 (12.8) を呼び出す場合があります。— 終了注記]

フォームで発生する初期化

   T  x(a);
   T  x{a};

new 式 (5.3.4)、 static_cast 式 (5.2.9)、関数表記型変換 (5.2.3)、および基本およびメンバー初期化子 (12.6.2) と同様に、直接初期化と呼ばれます。

T x{a}構文は C++0x で新しくなり、他のすべての規則は C++03 から変更されていません。


次に、次の規則が適用されます (同じセクション)。

  • 初期化が直接初期化の場合、またはソース型の cv 非修飾バージョンが宛先のクラスと同じクラスまたは派生クラスであるコピー初期化の場合、コンストラクターが考慮されます。適用可能なコンストラクターが列挙され (13.3.1.3)、オーバーロード解決 (13.3) によって最適なコンストラクターが選択されます。そのように選択されたコンストラクターは、引数として初期化式または式リストを使用して、オブジェクトを初期化するために呼び出されます。コンストラクターが適用されない場合、またはオーバーロードの解決があいまいな場合、初期化は不適切な形式です。

  • それ以外の場合 (つまり、残りのコピー初期化の場合)、ソース型から宛先型に、または (変換関数が使用されている場合は) その派生クラスに変換できるユーザー定義の変換シーケンスは、13.3 で説明されているように列挙されます。 1.4 であり、過負荷解決 (13.3) によって最適なものが選択されます。変換ができないか、あいまいな場合は、初期化の形式が正しくありません。 選択された関数は、初期化式を引数として呼び出されます。関数がコンストラクターの場合、呼び出しは、目的の型の cv 修飾されていないバージョンの一時を初期化します。一時的なものは prvalue です。呼び出しの結果 (コンストラクターの場合は一時的なもの) は、上記の規則に従って、コピー初期化の宛先であるオブジェクトを直接初期化するために使用されます。 場合によっては、中間結果を初期化されるオブジェクトに直接構築することにより、この直接初期化に固有のコピーを排除する実装が許可されます。12.2、12.8を参照。

于 2011-06-15T13:33:47.837 に答える