[解決済み] std::move()はどのようにRValuesに値を転送するのですか?
質問
の論理を十分に理解していないことに気づきました。
std::move()
.
とりあえずググってみましたが、どうやら
std::move()
の使い方についてのドキュメントがあるだけで、その構造がどのように動作するかについては書かれていないようです。
つまり、テンプレート・メンバー関数が何であるかは知っているのですが、それを調べると
std::move()
の定義をVS2010で見ると、まだ混乱しています。
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);
}
まず変なのは、パラメータである(_Ty&& _Arg)ですが、以下のように関数を呼び出すと
// main()
Object obj1;
Object obj2 = std::move(obj1);
というのは、基本的に
// std::move()
_Ty&& _Arg = Obj1;
しかし、すでにご存知のように、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++ についての基本的な構文スキルを欠いているためであることは分かっています。 これらがどのように動作するのか徹底的に知りたいので、インターネット上で得られるどんな文書でも大歓迎です。(もし、あなたがこれを説明してくれるなら、それも素晴らしいことです)。
どのように解決するのですか?
まず、移動関数から始めます(ちょっとだけきれいにしました)。
template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
return static_cast<typename remove_reference<T>::type&&>(arg);
}
簡単な部分から始めましょう。つまり、関数がrvalueで呼び出された場合です。
Object a = std::move(Object());
// Object() is temporary, which is prvalue
と、私たちの
move
テンプレートは次のようにインスタンス化されます。
// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
return static_cast<remove_reference<Object>::type&&>(arg);
}
以降
remove_reference
は変換します。
T&
を
T
または
T&&
から
T
で、そして
Object
は参照ではなく、最終的な関数が参照となります。
Object&& move(Object&& arg)
{
return static_cast<Object&&>(arg);
}
さて、キャストは必要なのだろうかと疑問に思うかもしれません。答えは、「はい、必要です」です。理由は簡単で、名前付きrvalue参照 は はlvalueとして扱われるからです(そして、lvalueからrvalue参照への暗黙の変換は標準では禁止されています)。
を呼び出すと、次のようになります。
move
をlvalueで呼び出すとこうなります。
Object a; // a is lvalue
Object b = std::move(a);
と対応する
move
のインスタンス化です。
// move with [T = Object&]
remove_reference<Object&>::type&& move(Object& && arg)
{
return static_cast<remove_reference<Object&>::type&&>(arg);
}
もう一度
remove_reference
は変換します。
Object&
を
Object
となり、得られる。
Object&& move(Object& && arg)
{
return static_cast<Object&&>(arg);
}
さて、ここからが厄介なところです。
Object& &&
は何を意味し、どのようにlvalueにバインドすることができるのでしょうか?
完全な転送を可能にするために、C++11 標準では参照の折りたたみに関する特別な規則が用意されており、それは次のとおりです。
Object & & = Object &
Object & && = Object &
Object && & = Object &
Object && && = Object &&
ご覧の通り、このルールの下では
Object& &&
は実際には
Object&
を意味します。これは、L値をバインドすることができるプレーンなL値参照です。
最終的な関数はこのようになります。
Object&& move(Object& arg)
{
return static_cast<Object&&>(arg);
}
は、前のrvalueを使ったインスタンス化と同じように、どちらも引数をrvalueの参照にキャストして、それを返します。違いは、最初のインスタンス化はrvalueでのみ使用でき、2番目のものはlvalueで動作することです。
なぜ
remove_reference
が必要なのかをもう少し説明するために、次の関数を試してみましょう。
template <typename T>
T&& wanna_be_move(T&& arg)
{
return static_cast<T&&>(arg);
}
を作成し、lvalueでインスタンス化します。
// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
return static_cast<Object& &&>(arg);
}
上記の参照折りたたみのルールを適用すると、以下のように使えない関数が得られることがわかります。
move
(として使えない関数が得られることがわかります(簡単に言うと、lvalueで呼ぶとlvalueが返ってくるということです)。どちらかというと、この関数はidentity関数です。
Object& wanna_be_move(Object& arg)
{
return static_cast<Object&>(arg);
}
関連
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】Visual Studioのデバッガーエラー。プログラムを開始できません 指定されたファイルが見つかりません
-
[解決済み] std::string を const char* または char* に変換する方法
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] std::stringのインスタンスを小文字に変換する方法
-
[解決済み] std::move()とは何ですか?また、どのような場合に使用するのですか?
-
[解決済み] C++11 rvalues と移動セマンティクスの混乱(return 文)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++のGetlineの問題(オーバーロードされた関数 "getline "のインスタンスがない
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】「corrupted size vs. prev_size」glibc エラーを理解する。
-
[解決済み】デバッグアサーションに失敗しました。C++のベクトル添え字が範囲外
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み] 変数サイズのオブジェクトが初期化されないことがある c++