11

C++11の移動セマンティクスに関する美しい記事を読みました。この記事は非常に直感的な方法で書かれています。この記事のクラスの例を以下に示します。

class ArrayWrapper 
{ 
public: 
    // default constructor produces a moderately sized array 
    ArrayWrapper () 
        : _p_vals( new int[ 64 ] ) 
        , _metadata( 64, "ArrayWrapper" ) 
    {} 

    ArrayWrapper (int n) 
        : _p_vals( new int[ n ] ) 
        , _metadata( n, "ArrayWrapper" ) 
    {} 

    // move constructor 
    ArrayWrapper (ArrayWrapper&& other) 
        : _p_vals( other._p_vals  ) 
        , _metadata( other._metadata ) 
    { 
        other._p_vals = NULL; 
    } 

    // copy constructor 
    ArrayWrapper (const ArrayWrapper& other) 
        : _p_vals( new int[ other._metadata.getSize() ] ) 
        , _metadata( other._metadata ) 
    { 
        for ( int i = 0; i < _metadata.getSize(); ++i ) 
        { 
            _p_vals[ i ] = other._p_vals[ i ]; 
        } 
    } 
    ~ArrayWrapper () 
    { 
        delete [] _p_vals; 
    } 
private: 
    int *_p_vals; 
    MetaData _metadata; 
};

明らかに、上記の移動コンストラクターの実装では、埋め込み要素に対して移動は発生しません_metadata。これを容易にするための秘訣は、std::move()このような方法を使用することです。

ArrayWrapper (ArrayWrapper&& other) 
        : _p_vals( other._p_vals  ) 
        , _metadata( std::move( other._metadata ) ) 
{ 
    other._p_vals = NULL; 
} 

ここまでは順調ですね。

標準は言う:

§5(C++11§5[expr]/ 6):

[注:次の場合、式はxvalueです。

  • 暗黙的または明示的に、戻り型がオブジェクト型への右辺値参照である関数を呼び出した結果。

  • オブジェクト型への右辺値参照へのキャスト、

  • オブジェクト式がxvalueである非参照型の非静的データメンバーを指定するクラスメンバーアクセス式、または

  • 最初のオペランドがxvalueであり、2番目のオペランドがデータメンバーへの.*ポインターであるメンバーへのポインター式。

私の質問:

さて、othermoveコンストラクターの変数はxvalueです(私は正しいですか?)。次に、上記の最後のルールに従って、other._metadataもxvalueである必要があります。したがって、コンパイラは、_metadataのクラスのmoveコンストラクタを暗黙的に使用できます。だから、std::moveここにいる必要はありません。

私は何が欠けていますか?

4

2 に答える 2

15

あなたの仮定は本当に真実ではありません。コンストラクターへの引数は、です。これにより、右辺値参照をバインドできますが、右辺値参照がバインドされると、コンストラクター内では、それxvalueはもはや。になります。概念的には、呼び出し場所のオブジェクトは期限切れになりますが、コンストラクター内で、コンストラクターブロック内で後で使用できるため、コンストラクター内で完了するまでは期限切れになりません。xvaluelvalue

ArrayWrapper f();
ArrayWrapper r = f();   // [1]

[1]では、式f()は一時的なものを参照しており、コンストラクターの呼び出し後に期限切れになるため、右辺値参照でバインドできます。

ArrayWrapper (ArrayWrapper&& other) 
    : _p_vals( other._p_vals  ) 
    , _metadata( other._metadata )        // [2] 
{ 
    other._p_vals = NULL; 
    std::cout << other._metadata << "\n"; // [3]
} 

コンストラクター内でotherは、有効期限が切れていません。コンストラクターのすべての命令に対して有効です。コンパイラが[2]での移動を許可した場合、[3]で変数をさらに使用する可能性は無効になります。値を今すぐ​​期限切れにすることをコンパイラーに明示的に指示する必要があります。

于 2012-07-30T15:37:17.447 に答える
9

otherは変数であるため、左辺値です。名前付き参照は、参照の種類に関係なく、左辺値です。

于 2012-07-30T15:32:25.680 に答える