2016-08-09 4 views
3

C++でラムダ関数を使用すると、値によって取り込まれる変数はcopy of the valueを意味します。C++ラムダは実際にコピーによってキャプチャされたパラメータのコピーを作成しますか?

ラムダ関数内の値を変更しないと良いコンパイラを使用していれば、コードをコンパイルして最適化しても実際のコピーがないことを期待できますか?

たとえば、読み取り専用モードで使用されているので、new_itemを値として渡すのが理にかなっているようです。

void loadavg_file::add(loadavg_item const & new_item) 
{ 
    auto const & it(std::find_if(
      f_items.begin(), 
      f_items.end(), 
      [new_item](auto const & item) 
      { 
       return (item.f_address == new_item.f_address); 
      })); 

    if(it == f_items.end()) 
    { 
     f_items.push_back(it); 
    } 
    else 
    { 
     // replace existing item with new avg and timestamp 
     it->f_timestamp = new_item.f_timestamp; 
     it->f_avg = new_item.f_avg; 
    } 
} 

は、ループが実行に最適化され、new_itemの全くのコピーにつながることでしょうか?

+6

保証はありません。良いコンパイラ*はコピーを最適化するかもしれませんが、必須ではありません。 –

+5

あなたが知っている唯一の事柄は、コードがそのように振舞う必要があることです。すべてをインライン展開するときとコピー/オブジェクトを作成しないとき、そしてあなたが書いたことの間に違いがなければ、コンパイラはそれを行うことができます。それは?コンパイルしてアセンブリをチェックしてください。 – NathanOliver

+2

この質問は他の2人のコメント作成者よりも私にとって面白いようです。 AFAIK、コンパイラは特定の条件でのみコピーを削除することが許可されています。ラムダキャプチャがその1つであるかどうかはわかりません。 (コピーが些細な場合は問題ありませんが、OPはおそらくコピーが些細なものかどうかは気にしません)。 – zneak

答えて

6

new_item(つまりloadavg_item::loadavg_item(loadavg_item const&))のコピーコンストラクタにメモリの割り当て以外の効果がある場合は、それらの効果が発生することが観察されなければなりません(実際にはそれらを観察しようとする限り)。

これは、発生する副作用に依存している可能性があります。これは、コピーエリミッションが許可されているコンテキストではないためです。 が関数からの値をに戻すときにのみ、コピー・エリミッションが許可されます(そして、後者は必須です)。一方、メモリ割り当てエリートはどこでも許可されます([expr.new]/10のルールに従います)。 clangはこれで特に優れています。

生成されたアセンブリを検査することは、副作用の観測としてカウントされず、デバッガでプログラムを実行することもありません。

new_itemのコピーコンストラクタが非インラインである場合、トランスレーションユニットのアセンブリがシンボルとしてコピーコンストラクタを呼び出すことがありますが、リンク時間最適化(LTO) -timeオプティマイザは、コピーコンストラクタに観察可能な副作用がないことを推論できます。

+0

これは、参照を使用すると最適化の可能性が増す可能性が高いことを意味しますか?言い換えれば、C++プログラマは、ほとんどの場合、ラムダ関数が実際にオブジェクトのコピーを変更する場合を除いて、最適化のために参照を使用する必要がありますか? –

+1

@Alexis:私はそれをそのように表現しません。コンパイラーはエイリアシングを伴うケースを処理する必要があるため、参照はコピーを避けるために保証されており、他の最適化も終了する可能性があります。 –

1

はい、 コピーには コピーが2つあります(ラムダは値渡され、そのメンバーは再度コピーされるため)。

全体コールツリー(find_if、コンストラクタ、デストラクタ、operator==、任意の関数これらのコールをコピー)は、可視機能とコンパイラで構成されている場合、それらをインライン化することを選択し、そのような共通部分式脱離などのさらなる最適化を得ることが可能ですこれらのコピーのランタイムコストを削減または排除します。プロセスで

、コンパイラは(そのような変更が元に伝播してはならないので)コピーの

  • 値は
  • 変更しないことを証明する必要がありますコピーコンストラクタは何の側面を持っていません効果。
  • デストラクタには副作用がありません。
  • 関数のどれもオブジェクトのIDに依存しません。
  • コピーの有効期間は、パラメータで参照されるオブジェクトの有効期間を超えてはなりません。
  • パラメータによって参照されるオブジェクトに他のエイリアスは存在しません。または、他のスレッドからのアクセス(同期されている場合)を含むラムダが使用されている間は、他のエイリアスを使用してオブジェクトを変更できません。

コピーを避けたい場合は、そのコピーを求めるコードを書かないでください。または、オブジェクトの必要な部分のみをコピーします。

auto const & it(std::find_if(
     f_items.begin(), 
     f_items.end(), 
     [key = new_item.f_address](auto const & item) 
     { 
      return (item.f_address == key); 
     })); 
関連する問題