2011-09-22 14 views
66

私はちょうどstd::move()の論理を完全に理解していません。std :: move()は値をRValuesにどのように転送しますか?

まず、私はそれを見つけましたが、その構造の仕組みではなく、std::move()の使用方法に関する文書しかないようです。

私はテンプレートメンバーの機能を知っていますが、VS2010のstd::move()定義を調べると、それはまだ混乱しています。

std :: move()の定義は以下のとおりです。

template<class _Ty> inline 
typename tr1::_Remove_reference<_Ty>::_Type&& 
    move(_Ty&& _Arg) 
    { // forward _Arg as movable 
     return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg); 
    } 

あなたは以下を参照のように私は

// main() 
Object obj1; 
Object obj2 = std::move(obj1); 

、関数を呼び出すとき、それは基本的に

// std::move() 
_Ty&& _Arg = Obj1; 
に等しいので、どのような私には最初奇妙であることは、パラメータ、(_Ty & & _Arg)であります

あなたが知っているように、LValueをRValue参照に直接リンクすることはできません。そうすれば、これはこのようになるはずです。

_Ty&& _Arg = (Object&&)obj1; 

しかし、std :: move()はすべての値に対して機能する必要があるため、これは不合理です。

私はこれがどのように動作するかを十分に理解していると思いますので、これらの構造体も見てください。

template<class _Ty> 
struct _Remove_reference 
{ // remove reference 
    typedef _Ty _Type; 
}; 

template<class _Ty> 
struct _Remove_reference<_Ty&> 
{ // remove reference 
    typedef _Ty _Type; 
}; 

template<class _Ty> 
struct _Remove_reference<_Ty&&> 
{ // remove rvalue reference 
    typedef _Ty _Type; 
}; 

残念ながら、それはまだ混乱しており、私はそれを得ていません。

これは私のC++に関する基本的な構文スキルの欠如のためです。 私は、これらが徹底的にどのように働いているのか、そして私がインターネットで手に入れることができる文書が歓迎される以上のことを知りたいと思います。 (あなたがこれを説明することができれば、それも素晴らしいでしょう)。

+7

"これは私のC++に関する基本的な構文スキルの欠如のためです。"それから、あなたはそれがどのように機能するかを知る準備ができていません。あなたが知る必要があるのは、ムーブコンストラクタを書く方法と、何かを動かすときに 'std :: move'をいつどのように使うのかです。このプロセスの詳細、つまりC++ 11がこのプロセスを動作させるために使用した_ "深い魔法" _は、あなたが心配する必要があるものではありません。あなたは、すべての機能を実現する機能の特定のやりとりを理解するための知識の基礎を持っていないだけです。 –

+0

Nicol Bolasが言ったこと。あなたが基本的なスキルが不足していることを知っているなら、それは最初に修正する必要がある問題です。あなたはstd :: moveを理解できません。 –

+11

@ NicolBolasそれどころか、質問自体は、OPがその声明の中で自分自身をアンセートしていることを示しています。それは正確には、彼らが質問をすることさえできる基本的な構文スキルのOPの*把握*です。 –

答えて

111

我々は(私は少しクリーンアップ)移動機能を起動します。

template <typename T> 
typename remove_reference<T>::type&& move(T&& arg) 
{ 
    return static_cast<typename remove_reference<T>::type&&>(arg); 
} 

簡単にパートで始まるのをしてみましょう - 関数が右辺値で呼び出されたとき、である。

Object a = std::move(Object()); 
// Object() is temporary, which is prvalue 

と、次のように私たちのmoveテンプレートがインスタンス化されます:

remove_reference以来0

TからTまたはT&&T&変換し、Objectが参照されていない、私たちの最終的な機能は次のとおりです。今

Object&& move(Object&& arg) 
{ 
    return static_cast<Object&&>(arg); 
} 

、あなたは不思議に思うかもしれません:私たちも、キャストが必要なのでしょうか?答えは:はい、そうです。理由は簡単です。 rvalue reference という名前がであり、lvalueとして扱われます(また、lvalueからrvalueへの暗黙の変換は標準で禁止されています)。 remove_referenceObjectからObject&変換し、我々が得る、再び

// move with [T = Object&] 
remove_reference<Object&>::type&& move(Object& && arg) 
{ 
    return static_cast<remove_reference<Object&>::type&&>(arg); 
} 

Object a; // a is lvalue 
Object b = std::move(a); 

moveインスタンス化に対応:


は、ここでは左辺値でmoveを呼び出すときに何が起こるかだ

Object&& move(Object& && arg) 
{ 
    return static_cast<Object&&>(arg); 
} 

今、私たちは難しい部分に行きます:Object& &&は何の意味ですか、それは左辺値にどのようにバインドできますか?

完璧な転送を可能にするには、C++ 11標準は、以下の通りである。これは、崩壊参照のための特別なルールが用意されています

Object & & = Object & 
Object & && = Object & 
Object && & = Object & 
Object && && = Object && 

を使用すると、これらのルールObject& &&の下で、見ることができるように、実際にプレーンな左辺値であるObject&を意味し、結合lvaluesを可能にする参照。

最後の関数は、このようです:

右辺値と前回のインスタンス化とは違っていない
Object&& move(Object& arg) 
{ 
    return static_cast<Object&&>(arg); 
} 

- 彼らは両方の右辺値参照に引数をキャストし、それを返します。違いは、最初のインスタンス化は右辺値のみで使用でき、後者は左辺値と一緒に使用できることです。


我々はもう少しremove_referenceが必要なのか理由を説明する、のが

template <typename T> 
T&& wanna_be_move(T&& arg) 
{ 
    return static_cast<T&&>(arg); 
} 

この機能を試してみて、左辺値とそれをインスタンス化してみましょう。上記のルールを崩壊参照を適用

// wanna_be_move [with T = Object&] 
Object& && wanna_be_move(Object& && arg) 
{ 
    return static_cast<Object& &&>(arg); 
} 

、あなたは私たちが(簡単に言えば、あなたは左辺値取り戻す、左辺値でそれを呼び出す)moveとして使用できない機能を見ることができます。何かあれば、この関数はアイデンティティ関数です。

Object& wanna_be_move(Object& arg) 
{ 
    return static_cast<Object&>(arg); 
} 
+2

良い答え。左辺値は 'T 'を' Object'として評価することをお勧めしますが、これが実際に行われたかどうかはわかりませんでした。私は、これがラッパー参照と 'std :: ref'を導入する理由だと思ったので、' T'も 'Object'に評価すると思っていたでしょうか、そうではありませんでした。 –

+0

[このウィキペディアの記事](http://en.wikipedia.org/wiki/C%2B%2B11#Wrapper_reference)は古くなっていますか誤解されていますか? –

+0

これは本当に良い、明確な答え、+1です。 – Xeo

2

_Tyは、テンプレートパラメータであり、このような状況で

Object obj1; 
Object obj2 = std::move(obj1); 

_Tyは入力_Remove_referenceが必要な理由である

"&オブジェクト" です。

我々はcouldnれ、&をObjectに

Object&& obj2 = (ObjectRef&&)obj1_ref; 

しかしobjectrefに& &が減少をしていたようにそれは私たちが参照を削除しなかった場合、それは次のようになり、より

typedef Object& ObjectRef; 
Object obj1; 
ObjectRef&& obj1_ref = obj1; 
Object&& obj2 = (Object&&)obj1_ref; 

ようになりますobj2にバインドしません。

この方法を減らす理由は、完全な転送をサポートすることです。 this paperを参照してください。

+0

これは '_Remove_reference_'がなぜ必要なのかについては何も説明していません。たとえば、 'Object&'をtypedefとし、それを参照すると 'Object&'が得られます。なぜ&&で動作しないのですか?そこには答えがあり、完璧なフォワーディングと関係しています。 –

+2

真。答えはとても面白いです。 &&&はA&に縮小されていますので、(ObjectRef &&)obj1_refを使用しようとすると、この場合Object&が返されます。 –

関連する問題