8

直接初期化とコピー初期化の違いを読んでいました(§8.5/12):

T x(a);  //direct-initialization
T y = a; //copy-initialization

copy-initializationについて読んで理解したのは、アクセス可能で非明示的な copy-constructorが必要であるということです。そうしないと、プログラムがコンパイルされません。次のコードを書いて確認しました。

struct A
{
   int i;
       A(int i) : i(i) { std::cout << " A(int i)" << std::endl; }
   private:
       A(const A &a)  {  std::cout << " A(const A &)" << std::endl; }
};

int main() {
        A a = 10; //error - copy-ctor is private!
}

GCC は次のようなエラー ( ideone )を返します。

prog.cpp:8: エラー: 'A::A(const A&)' は非公開です

これまでのところ、すべて順調で、ハーブ・サッターの言葉を再確認しました。

コピーの初期化とは、必要に応じて最初にユーザー定義の変換を呼び出した後、コピー コンストラクターを使用してオブジェクトが初期化されることを意味し、"T t = u;" の形式と同等です。


privateその後、キーワードにコメントを付けて copy-ctor にアクセスできるようにしました。さて、当然のことながら、次のものが印刷されることを期待しています。

A(定数 A&)

しかし驚いたことに、代わりにこれを出力します ( ideone ):

A(int i)

なんで?

わかりました。まず、を使用して、必要に応じてここで変換規則を適用して (§8.5/14)、その型から型の一時オブジェクトを作成し、次に copy-ctor を呼び出して を初期化することになっていることを理解しAました。しかし、そうではありませんでした。なんで?10intA(int i)a

copy-constructor (§8.5/14) を呼び出す必要をなくす実装が許可されている場合、copy-constructor が宣言されているときにコードを受け入れないのはなぜprivateですか? 結局のところ、それはそれを呼んでいません。それは甘やかされて育った子供が、最初はいらいらして特定のおもちゃを要求し、あなたが彼に特定のおもちゃを与えると、あなたの後ろに捨ててしまうようなものです. :|

この動作は危険でしょうか? つまり、copy-ctor で何か他の便利なことをするかもしれませんが、それが呼び出されない場合、プログラムの動作は変更されませんか?

4

6 に答える 6

10

コンパイラがアクセスチェックを行う理由を尋ねていますか? C++03 の 12.8/14:

オブジェクトのコピー コンストラクターまたはコピー代入演算子が暗黙的に使用され、特別なメンバー関数にアクセスできない場合、プログラムは不正な形式です。

実装が「コピー構築を省略」した場合 (12.8/15 で許可)、これはコピー ctor が「暗黙的に使用」されなくなったことを意味するとは思いません。単に実行されないだけです。

それとも、標準がそう言っている理由を尋ねていますか?コピー省略がアクセス チェックに関するこの規則の例外である場合、プログラムは、省略を正常に実行する実装では整形式ですが、そうでない実装では整形式ではありません。

著者はこれを悪いことだと考えているに違いありません。確かに、移植性のあるコードをこのように書く方が簡単です。コピーできないオブジェクトをコピーしようとするコードを書くと、実装でコピーがたまたま省略されたとしても、コンパイラが教えてくれます。アクセスをチェックする前に最適化が成功するかどうかを判断する (または最適化が試行されるまでアクセス チェックを延期する) ことは、実装者に不便をもたらす可能性もあると思いますが、それが考慮に値するかどうかはわかりません。

この動作は危険でしょうか? つまり、copy-ctor で何か他の便利なことを行うかもしれませんが、それが呼び出されない場合、プログラムの動作は変更されませんか?

もちろん、それは危険である可能性があります - オブジェクトが実際にコピーされた場合にのみ、コピー コンストラクターの副作用が発生するため、それに応じて設計する必要があります。 12.8/15 で定義された条件の下で省略されることを喜んでいます。

MyObject(const MyObject &other) {
    std::cout << "copy " << (void*)(&other) << " to " << (void*)this << "\n"; // OK
    std::cout << "object returned from function\n"; // dangerous: if the copy is
      // elided then an object will be returned but you won't see the message.
}
于 2011-05-28T17:51:14.430 に答える
5

C ++では、プログラムのセマンティクスを実際に変更するコピーコンストラクターを含むいくつかの最適化が明示的に許可されています。(これは、プログラムのセマンティクスに影響を与えないほとんどの最適化とは対照的です)。特に、既存のオブジェクトが到達不能になることがわかっている場合、コンパイラが既存のオブジェクトをコピーするのではなく、再利用することを許可される場合がいくつかあります。これ(コピー構造)はそのようなケースの1つです。もう1つの同様のケースは、「戻り値の最適化」(RVO)です。この場合、関数の戻り値を保持する変数を宣言すると、C ++はそれを呼び出し元のフレームに割り当てることを選択できるため、必要ありません。関数の完了時に呼び出し元にコピーして戻します。

一般に、C ++では、副作用があるか、単にコピー以外のことを行うコピーコンストラクターを定義すると、火遊びをします。

于 2011-05-28T18:02:05.083 に答える
4

どのコンパイラでも、コードの最適化プロセスの前に、構文 [およびセマンティック] 分析プロセスが実行されます。

コードは構文的に有効である必要があり、そうでなければコンパイルさえできません。コンパイラが作成する一時的なものを除外することを決定するのは、後のフェーズ (つまり、コードの最適化) だけです。

したがって、アクセス可能なコピー c-tor が必要です。

于 2011-05-28T17:20:59.277 に答える
1

ここでこれを見つけることができます(あなたのコメントで;)):

[標準]では、一時コピーを削除できるとされていますが、コピーコンストラクターのセマンティック制約(アクセシビリティなど)をチェックする必要があります。

于 2011-05-28T17:41:16.753 に答える
0

RVOとNRVO、相棒。コピー省略の完璧なケース。

于 2011-05-28T17:04:26.063 に答える
0

これはコンパイラによる最適化です。

評価中:A a = 10;代わりに:

  1. 最初に一時オブジェクトを構築しますA(int)

  2. コピーコンストラクターaを介して構築し、一時的に渡します。

コンパイラは単純にaを使用して構築しA(int)ます。

于 2011-05-28T17:09:42.367 に答える