3

次の簡単なコードを GCC 4.7.2 (MinGW) でコンパイルしようとしています。ここでは、C++11 機能を使用しています - 非静的メンバー初期化子:

#include <iostream>
using namespace std;

struct A
{
    int var;

    A()
    {
        cout << "A()\n";
    }

    A(int i)
    {
        cout << "A(int i)\n";
        var = i;
    }

    A(const A&) = delete;
};

struct B
{
    A a = 7;
};

int main()
{
    B b;
    cout << "b.a.var = " << b.a.var;
    return 0;
}

ここでは不要なコピー コンストラクターが削除されているため、このコードはコンパイルされません。エラーは次のとおりです。

main.cpp:27:11: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
main.cpp: In constructor 'constexpr B::B()':
main.cpp:25:8: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here

次のようにコピーコンストラクターを実装すると:

A(const A& a)
{
    cout << "A(const A&)\n";
    var = a.var;
}

その後、コードは正常にコンパイルされ、プログラムは期待される出力を提供します:

A(int i)
b.a.var = 7

つまり、コピー コンストラクターが使用されていないということですが、なぜ削除できないのでしょうか。

編集:回答ありがとうございます。を使用している場合、標準でコンストラクターのコピーまたは移動が必要です=。この問題を解決するには、ムーブ コンストラクターを実装するか、直接初期化構文を使用する必要がありますA a{7}

4

5 に答える 5

4

の初期化子aは、コピー初期化を提供します。

A a = 7;

このようなコピー初期化の場合、ユーザー定義の変換が必要な場合、結果の初期化は次のようになります。

A a(A(7));

つまり、テンポラリAが構築され、オブジェクトのコピー コンストラクタに渡されaます。このコピーは省略できますが、それでもコピー コンストラクターは使用可能でなければなりません。つまり、そもそもコピーが可能である場合にのみ、コピーを省略できます。コピー コンストラクタの場合delete、コピーは不可能です。

次のことを行うと、削除されたコピー コンストラクターをより快適に使用できます。

A a{7};

これは直接初期化を行い、コピー コンストラクターは必要ありません。

于 2013-02-16T17:09:30.093 に答える
3

コピーの初期化はコピーを省略できますが、コピー コンストラクターは標準によってアクセス可能であることが義務付けられています。

于 2013-02-16T17:03:45.067 に答える
2

C++11 標準のパラグラフ 12.2/14 によると:

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

T x = a;

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

コピー初期化がコンパイルされない理由は、コピー初期化中に一時オブジェクトを (少なくとも論理的に) 作成する必要があり、初期化されるオブジェクトはそれから構築されるためです。

これまでの回答はすべて copy-constructors だけに焦点を当てているように見えますが、ここでの最初の問題はmove-constructorがないことです。コピー コンストラクターを提供する限り、コピー コンストラクターは必要ありません。

残念ながら、コピー コンストラクターを削除すると、暗黙的な移動コンストラクターが生成されなくなります。明示的に追加すると、問題が解決します。

struct A
{
    int var;

    A()
    {
        cout << "A()\n";
    }

    A(int i)
    {
        cout << "A(int i)\n";
        var = i;
    }

    A(const A&) = delete;

    // THIS MAKES IT WORK
    A(A&& a)
    {
        cout << "A(A&&)\n`;
        var = a.var;
    }
};

move-constructor と copy-constructor の両方が存在する場合、 move-constructor が優先されることに注意してください。これは、オブジェクトをコピー初期化するために作成された一時が右辺値であるためです。

move-constructor が存在しない場合、コンパイラは copy コンストラクターを呼び出して初期化を実行できます。これは、定数の左辺値参照が右辺値参照にバインドでき、copy が最適化されていない移動と見なされるためです。

ただし、コンパイラがmove コンストラクターまたは copy コンストラクターの呼び出しを省略できる場合でも、操作のセマンティクスをチェックする必要があります。C++11 標準のパラグラフ 12.8/32 によると:

コピー操作の省略の基準が満たされているか、ソース オブジェクトが関数パラメーターであり、コピーされるオブジェクトが左辺値によって指定されているという事実を除いて満たされる場合、コピーのコンストラクターを選択するためのオーバーロードの解決は次のとおりです。オブジェクトが右辺値によって指定されたかのように最初に実行されます。オーバーロードの解決が失敗した場合、または選択されたコンストラクターの最初のパラメーターの型がオブジェクトの型 (おそらく cv 修飾) への右辺値参照でない場合、オブジェクトを左辺値と見なしてオーバーロードの解決が再度実行されます。[注: この 2 段階のオーバーロード解決は、コピーの省略が発生するかどうかに関係なく実行する必要があります。省略が実行されない場合に呼び出されるコンストラクターを決定し、呼び出しが省略された場合でも、選択されたコンストラクターにアクセスできる必要があります。—エンドノート] [...]

したがって、移動コンストラクターもコピー コンストラクターも存在しない場合、コンパイラによってエラーが発行されます。

ただし、必要に応じて、オブジェクトをコピー初期化するのではなく、直接初期化することができます。代わりに直接初期化構文を使用してください。

struct B
{
    A a{7};
};

これにより、オブジェクトを直接初期化するときに一時的なものが作成されないため、move-constructor と copy-constructor が不要になります。

于 2013-02-16T17:38:45.297 に答える
1

ということは、コピー コンストラクタが使用されていないということですが、なぜ削除できないのでしょうか。

あなたの場合、コピーコンストラクターは、標準で必要なセマンティックチェックにのみ使用され、アクセス可能である必要もあります。後で、コンパイラはコードを最適化し、コピー コンストラクターの呼び出しを除外するため、実際には呼び出されません。

于 2013-02-16T17:03:31.463 に答える
1

ここでは不要なコピー コンストラクターが削除されているため、このコードはコンパイルされません。

申し訳ありませんが、コピー コンストラクター必要です。コピーを最適化して除外することはできますが、コード内ではまだ可能でなければなりません。これは言語によって義務付けられています。

于 2013-02-16T17:07:53.133 に答える