2012-05-31 11 views
14

私は単純なクラスがあります:私は最近、右辺値について少し読んだ、と私は右辺値を使用してXのコンストラクタを書く必要がある場合、私は、思ってきたので、私は一時的に検出しますことができるだろうstd :: stringのrvaluesを使ってコンストラクタを書くべきでしょうか?

class X 
{ 
    std::string S; 
    X (const std::string& s) : S(s) { } 
}; 

std::stringタイプのオブジェクト?使用可能な場合、コンストラクタを移動する私の知識、それを使用する必要がありますC++ 11を支持コンパイラでのstd ::文字列の実装については

X (std::string&& s) : S(s) { } 

です:

は、私はそれがようになるはずだと思います。

+2

[関連](http://stackoverflow.com/questions/7592630/is-pass-by-value-a-reasonable-default-in-c11)。 –

+0

引用符で囲まれたコードは、呼び出し側が 'X()'にrvalueを渡す必要があることを制約しますが、 'S'のコンストラクタにrvaluenessを渡すことができません。したがって移動することはできず、コピーする必要があります。したがって、関数のユーザーに負担をかける一方で、代償として利益を得られないようにします。 –

+0

@underscore_d私は5年後、私のコードが完璧ではないという答えで合意に達したと思う;) –

答えて

23
X (std::string&& s) : S(s) { } 

右辺を取るコンストラクタが、右辺値参照を取るコンストラクタされていないこと。この場合、右辺値参照を使用しないでください。むしろ値とメンバーへのその後移動で渡す:

X (std::string s) : S(std::move(s)) { } 

親指のルールは、あなたがコピーする必要がある場合は、インターフェイスでそれを行うことです。

+3

なぜこの場合、rvalue-referenceを受け取ったコンストラクタを持つべきではありませんか? – ncasas

+2

@ncasas:コンビナトリアル爆発? –

+6

@ncasas:次に、 'X(std :: string &&)'と 'X(std :: string const&)'を提供する必要があります(オブジェクトを構築するために使用される引数が* lvalue * )。代わりのものは* pass-by-value *コンストラクタでコンパイラによって簡単に管理され、パラメータは 'std :: string(std :: string const&)'または 'std :: string(std :: string &&) 'を使ってコピーを作成します。つまり、値渡しのコンストラクタのみを指定すると、ライブラリの書き換えを行わずに、ライブラリの機能を使用することができます。 –

13

いいえ、そうしないでください。何をすべきことは、このようなものを使用して、現在のコンストラクタを置き換えです:

X (std::string s) :S(std::move(s)) {} 

は今、あなたは、あなたのクラスの文字列に移動したパラメータにコピーされますL値、およびr値の両方を扱うことができ、これは2回移動されます(あなたのコンパイラはこの余分な作業を遠く離れて最適化することができます)。

ほとんどの場合(ここでは例外はありません)、書き込むクラスの移動コンストラクタを除いて、r値参照を取る関数を書くべきではありません。独自の値のコピーが必要なときはいつでも、これはコンストラクターには当てはまりません。価値のあるものを取り込み、必要な場所に移動する必要があります。クラス自身の移動コンストラクタに、r値またはl値のどちらを受け取るかによって値をコピーするか移動するかを決定させます。結局のところ、r値参照は私たちの生活をより簡単に、より困難にするために導入されました。

+0

私は、rvalue-referencesがmoveコンストラクタ(そして明らかに標準ライブラリによって提供される 'move'と' forward'関数)とは別の用途を持っていたかどうかを正確に考えていました。 rvalue-referencesを使うべき他のいくつかの事例をご存知ですか? –

+0

@LucTouraille:完全転送を可能にするテンプレート関数です。例えば'emplace_back'、' make_shared' –

+0

ああ、もちろん!私は 'forward'を呼び起こし、その目的については考えていませんでした。 –

5

引数のコピーが必要なので、パラメータを値で指定します。次に、メンバーデータに移動します。指定された引数が右辺値か左辺値かを検出するのは、std::stringコンストラクタです。

class X 
{ 
    std::string s_; 
    X(std::string s) : s_(std::move(s)) {} 
}; 
19

明確化のために:パスバイバリューの回答は間違っていません。

追加:

X (std::string&& s) : S(std::move(s)) { } 

すなわちしかし、どちらも1つの詳細を除いて、string&&過負荷を加えることのあなたの最初の推測ではありますsstringへの右辺値参照の宣言された型を持っているがために、あなたはまだSを初期化するために使用表現sはタイプstringの左辺値式で、moveを必要としています。

実際、最初に提案した(移動が追加された)ソリューションは、値渡しソリューションよりもわずかに高速です。しかし、どちらも正しいです。値渡しの解決策は、lvalue引数とxvalue引数をXのコンストラクタに渡すときにstringのmoveコンストラクタを余分な時間を呼び出します。

lvalue引数の場合は、とにかくコピーが作成され、stringのコピーコンストラクタは、文字列の移動コンストラクタよりもはるかに高価になる可能性があります(短い文字列バッファに収まる文字列を除きます。ほぼ同じ速度です)。

xvalue引数の場合(xvalueはstd::moveに渡された左辺値)、値渡しのソリューションでは1つではなく2つの移動構成が必要です。したがって、それはrvalue参照解によるパスの2倍の高さです。しかし、まだ非常に速い。

この投稿のポイントは、値渡しが許容される解決策であることを明確にすることです。しかし、これは唯一の許容可能な解決策ではありません。 pass-by-rvalue-refを使ったオーバーロードは高速ですが、オーバーロードの回数が引数Nの数が増えるにつれて2^Nのスケールが必要であるという欠点があります。

関連する問題