33

今夜、私は過去数日間に取り組んできたいくつかのコードを調べ、moveセマンティクス、特にstd::moveについて読み始めました。私が正しい道を進んでいて、愚かな仮定をしないことを確実にするためにあなたにプロに尋ねるいくつかの質問があります!

まず:

1)元々、私のコードには大きなベクトルを返す関数がありました。

template<class T> class MyObject
{
public:
    std::vector<T> doSomething() const;
    {
        std::vector<T> theVector;

        // produce/work with a vector right here

        return(theVector);
    }; // eo doSomething
};  // eo class MyObject

「theVector」がこれと「throw-away」で一時的であるとすると、関数を次のように変更しました。

    std::vector<T>&& doSomething() const;
    {
        std::vector<T> theVector;

        // produce/work with a vector right here

        return(static_cast<std::vector<T>&&>(theVector));
    }; // eo doSomething

これは正しいです?このようにすることの落とし穴はありますか?

std::string2)ある関数で、moveコンストラクターが自動的に呼び出されることを返すことに気づきました。文字列の戻り値(ありがとう、アラゴルン)にデバッグすると、明示的な移動コンストラクターと呼ばれることに気づきました。ベクトルではなく文字列クラス用のものがあるのはなぜですか?

移動セマンティクスを利用するために、この関数に変更を加える必要はありませんでした。

// below, no need for std::string&& return value?
std::string AnyConverter::toString(const boost::any& _val) const
{
    string ret;
    // convert here
    return(ret); // No need for static_cast<std::string&&> ?
}; // eo toString

3)最後に、いくつかのパフォーマンステストを実行したかったのですが、std :: moveセマンティクスが原因で得られた驚くほど高速な結果ですか、それともコンパイラ(VS2010)も最適化を実行しましたか?

_getMilliseconds()(簡潔にするために省略の実装)

std::vector<int> v;
for(int a(0); a < 1000000; ++a)
    v.push_back(a);

std::vector<int> x;
for(int a(0); a < 1000000; ++a)
    x.push_back(a);

    int s1 = _getMilliseconds();
std::vector<int> v2 = v;
    int s2 =  _getMilliseconds();
std::vector<int> v3 = std::move(x);
    int s3 =  _getMilliseconds();

    int result1 = s2 - s1;
    int result2 = s3 - s2;

結果は明らかに素晴らしいものでした。result1は、標準の割り当てで、630ミリ秒かかりました。2番目の結果は0msでした。これはこれらのことの良いパフォーマンステストですか?

これのいくつかは多くの人にとって明らかなことですが、コードをブレザーする直前にセマンティクスを理解していることを確認したいと思います。

前もって感謝します!

4

4 に答える 4

36

参照は引き続き参照です。同様に、C ++ 03ではローカルへの参照を返すことはできません(またはUBを取得することはできません)。C++0xでは返すことはできません。死んだオブジェクトへの参照になってしまいます。たまたま右辺値の参照です。だからこれは間違っています:

std::vector<T>&& doSomething() const
{
    std::vector<T> local;

    return local; // oops
    return std::move(local); // also oops
}

2番目に見たものを実行する必要があります。

// okay, return by-value 
std::vector<T> doSomething() const
{
    std::vector<T> local;

    return local; // exactly the same as:
    return std::move(local); // move-construct value
}

関数にローカルな変数は、戻ったときに一時的なものであるため、コードを変更する必要はありません。リターンタイプは、移動セマンティクスの実装を担当するものであり、あなたではありません。

テストのように、通常は実行されない場合に、何かstd::move明示的に移動するために使用する必要があります。(どちらが問題ないようです。リリースの場合でしたか?ベクターの内容を出力する必要があります。そうしないと、コンパイラーが最適化します。)

右辺値の参照について知りたい場合は、こちらをお読みください

于 2010-11-10T19:47:24.630 に答える
14
return(theVector);

theVectorはローカルオブジェクトであるため、これは特別な言語ルールのためにすでに暗黙的に移動しています。セクション12.8のパラグラフ34および35を参照してください。

特定の基準が満たされると、オブジェクトのコピー/移動コンストラクタおよび/またはデストラクタに副作用がある場合でも、実装はクラスオブジェクトのコピー/移動構築を省略できます。このような場合、実装は、省略されたコピー/移動操作のソースとターゲットを、同じオブジェクトを参照する2つの異なる方法として扱い、そのオブジェクトの破棄は、2つのオブジェクトが最適化なしで破棄されました。コピーの省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます(複数のコピーを削除するために組み合わせることができます)。

クラスreturn型の関数のreturnステートメントで、式が関数return型と同じcv-unqualified型の不揮発性自動オブジェクトの名前である場合、コピー/移動操作は次のように構成することで省略できます。関数の戻り値に直接自動オブジェクト

[...]

コピー操作の省略基準が満たされ、コピーされるオブジェクトが左辺値で指定されると、オブジェクトが右辺値で指定されているかのように、コピーのコンストラクターを選択するためのオーバーロード解決が最初に実行されます

参照による)ではなくstd::vector<T>値による)を返す必要があることに注意してください。std::vector<T>&&

しかし、なぜ括弧は?return関数ではありません:

return theVector;
于 2010-11-10T19:46:57.087 に答える
7

GManの答えに追加するには、returnタイプをstd::vector<T>(参照なしで、そうでない場合はUBを取得します)に変更しても、「1)」のreturn式を変更してもパフォーマンスは向上しませんが、パフォーマンスが向上する可能性があります。少し悪い。移動コンストラクターと同様std::vectorに、ローカルオブジェクトを返す場合、、、、またはを記述しても、vectorのコピーコンストラクターは呼び出されません。最後の2つのケースでは、コンパイラーはmoveコンストラクターを呼び出すように強制されます。ただし、最初のケースでは、その機能に対してNRVOを実行できる場合は、移動を完全に最適化する自由があります。何らかの理由でNRVOが不可能な場合にのみ、コンパイラーはmoveコンストラクターの呼び出しに頼ります。だからに変更しないでくださいreturn theVector;return static_cast<std::vector<T>&&>(theVector);return std::move(theVector)return x;return std::move(x);xは、返される関数内のローカルの非静的オブジェクトです。そうしないと、コンパイラが別の最適化の機会を使用できなくなります。

于 2010-11-10T19:58:17.433 に答える
5

何かを移動する標準的な方法は、std::move(x)ではなく、を使用することstatic_castです。AFAIK、名前付き戻り値の最適化は、値ごとにベクトルを返すことから始まる可能性が高いため、移動セマンティクスの前にも十分に機能していました。

パフォーマンステストは、ムーブセマンティクスがパフォーマンスにどのように役立つかを示す良い例です。コピー代入は100万個の要素をコピーする必要があり、ムーブ代入は基本的にベクトルの内部ポインターを入れ替えるだけです。これは簡単な単語の割り当てです。ほんの数サイクル。

于 2010-11-10T19:46:17.927 に答える