2012-04-01 12 views
10

週末を過ごす私はC++のスキルをリフレッシュして、いくつかのC++ 11を学びたいと思っています。次の問題に遭遇しました。使用ムーブコンストラクタ:次のように定義され、std :: vectorをコンストラクタに渡してセマンティクスを移動する

class builder 
{ 
    ... 
    container build() const 
    { 
    std::vector<items> items; 

    //... fill up the vector 

    return container(items); //should move the vector right? wrong! 
    //return container(std::move(items)); also doesn't work 
    } 
} 

とクラスのアイテムとコンテナ:

は、私は次のように定義されたビルダークラスを、持っている

class container 
{ 
public: 

    container(std:vector<item> items) 
     : items_(items) // always invokes copy constructor on vector, never move 
    { } 

    container(container&& rhs) 
    { 
     ... 
    } 

    ... 

private: 
    std::vector<item> items_; 

} 

class item 
{ 
public: 
    //move .ctor 
    item(item && rhs); 
    item& operator=(item && rhs); 

    //copy .ctor 
    item(const item& rhs); //this gets called instead of move .ctor 
    item& operator=(const item& rhs); 

    ... 
} 

今私のコードの使用するすべてのアイテムが最初に構築してからコピーされます

builder my_builder; 
... 
auto result = my_builder.build(); 

を...意味するものではあり

どうやってアイテムをコピーしないようにclassess次書くべき

?私はちょうど標準のポインタを使用するに戻って行く必要がありますか?

+0

'item2'とは何ですか? – Mankarse

+0

@Mankarseこれはタイプミスです。 – ghord

答えて

21

あなたのコードは、このように変更します。一般的には

container(std:vector<item2> items) // may copy OR move 
: items_(std::move(items)) // always moves 
{} 

:あなたが何かのあなた自身のコピーをしたい場合は、そのパラメータのリストにそのコピーを作成し、それがために必要がある場合にそれを移動します。発信者が既存のデータをコピーまたは移動するかどうかを決定するものにします。 (言い換えれば、途中でデータを移動しただけです)

また、return container(std::move(items));です。これまで言及していなかったのは、すべてのローカル変数がreturnステートメントで自動的に移動されたと誤解していたためですが、返された値のみが誤っているためです。 (だから、これは実際に動作するはずです:。return items;containerのコンストラクタがexplicitではないので)

+1

この一般的なルールの例外は、移動しても高価な非常に大きなオブジェクトを渡す場合、(値ではなく)値の参照を渡す必要があることです。渡されるオブジェクトのタイプに関係なく効率的であるように意図された汎用コードを書く。 – Mankarse

+0

誰かが後で使用されるベクターでこのコンストラクタを呼び出す場合はどうですか?ベクトルの内容をコンテナ内のものに移動し、オリジナルのベクトルからそれらを削除しないでください。 – ghord

+0

@Mankarse:ルールは "あなた自身のコピーが必要な場合は..."で始まっているので、ルールの例外としては見ません。コピーが必要ない場合は、コピーを作成しないでください。高価なものか必要ないものがあれば、それは起こるでしょう。 – GManNickG

3

はあなたのために、このテンプレートの動きに対応したクラスを書きました。それを調べれば、それを得るでしょう。

私はこのような値の引数を使用してのに対して、常にだ
/// <summary>Container.</summary> 
class Container { 
private: 
    // Here be data! 
    std::vector<unsigned char> _Bytes; 

public: 
    /// <summary>Default constructor.</summary> 
    Container(){ 
    } 

    /// <summary>Copy constructor.</summary> 
    Container(const Container& Copy){ 
     *this = Copy; 
    } 

    /// <summary>Copy assignment</summary> 
    Container& operator = (const Container& Copy){ 
     // Avoid self assignment 
     if(&Copy == this){ 
      return *this; 
     } 
     // Get copying 
     _Bytes = Copy._Bytes; // Copies _Bytes 
     return *this; 
    } 

    /// <summary>Move constructor</summary> 
    Container(Container&& Move){ 
     // You must do this to pass to move assignment 
     *this = std::move(Move); // <- Important 
    } 

    /// <summary>Move assignment</summary> 
    Container& operator = (Container&& Move){ 
     // Avoid self assignment 
     if(&Move == this){ 
      return *this; 
     } 
     // Get moving 
     std::swap(_Bytes, Move._Bytes); // Moves _Bytes 
     return *this; 
    } 
}; // class Container 

function(std:vector<item2> items) 

私はいつも使用して、次のいずれか、特に大きなデータコンテナの

function(const std:vector<item2>& items) 
function(std:vector<item2>& items) 
function(std:vector<item2>&& items) 

、そしてめったに:

function(std:vector<item2> items) 

ta、決してベクトル。

このように、あなたは何が起こるのかを制御しているので、私たちはすべてを制御するためにC++をやっているのです。

  • 書き込み可能なコピーが必要な場合は、const参照を変数にコピーしてください。
  • 読み取り専用の場合、const参照は新しいコピーを防止します。
  • オリジナルを編集する場合は、参照のみを使用してください。
  • あなたが怠惰であると感じたら小さなデータのために値の引数を使用してください。

明らかに、それはすべてあなたが何をしているかによって異なります。

私は自己教えられたC++開発者です。特にC++スラングの専門家からは遠いですが、学習:)

+1

[C++ 11で効率的な引数を渡す](http://www.codesynthesis.com/~boris/blog/2012/06/26/efficient-argument-passing-cxx11-part2/)...良い説明。 – CodeAngry

関連する問題