c++ std::move Principle の実装と使用法のまとめ
C++11では、標準ライブラリは<utility>に便利な関数std::moveを提供しています。std::moveは何も移動しません。その唯一の機能は、左値を右参照に強制変換し、右参照による移動セマンティクスに使用できるようにすることです。実装としては、std::move は基本的に static_cast<T&&>(lvalue) という型変換と等価です。
std::move関数は、非常に簡単な方法で左値参照を右値参照に変換します。(左値右値参照左値参照)の概念 https://blog.csdn.net/p942005405/article/details/84644101
- C++標準ライブラリでは、vector::push_backなどの関数を使って、引数のオブジェクトをコピーし、データもコピーしています。そのため、引数をpush_backするだけでよかったはずのオブジェクトメモリが余分に生成されてしまいますが、std::moveを使うことで不要なコピーを回避することができます。
- std::moveはオブジェクトの状態や所有権をあるオブジェクトから別のオブジェクトに転送するもので、単なる転送であり、メモリの再配置やメモリコピーは行わないので、より効率的に使用でき、パフォーマンスを向上させることができます。
- ポインタ型の標準ライブラリオブジェクトには必要ありません。
使用方法
元のlvalueの値から移動するので、空文字列になります。
//from https://zh.cppreference.com/w/cpp/utility/move
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
std::string str = "Hello";
std::vector<std::string> v;
// call the regular copy constructor, create a new array of characters, and copy the data
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// Call the move constructor, hollow out str, and after hollowing out, it is better not to use str
v.push_back(std::move(str));
std::cout << "After move, str is \"" << str << "\"\n";
std::cout << "The contents of the vector are \"" << << v[0]
<< "\", \"" << v[1] << "\"\n";
}
出力します。
After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"
std::move の関数プロトタイプ定義
template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
プロトタイプ定義における模式的な実装。
まず、関数の引数T&&は、テンプレート型の引数への右値参照である。参照畳み込みによって、この引数はどんなタイプの実参照にもマッチする(左値でも右値でも渡すことができ、これはstd::moveが使われる2つの主なシナリオである)。参照フォールディングについては、以下の通りである。
(式1) X& &, X&& &, X& & は左の値では X& に折りたたまれます。
string s("hello");
std::move(s) => std::move(string& &&) => after folding std::move(string& )
At this point: the type of T is string&
typename remove_reference<T>::type is string
The entire std::move is instantiated as follows
string&& move(string& t) //t is the left value, and cannot be used after moving t
{
//Forced conversion of string& to string&& by static_cast;
return static_cast<string&&>(t);
}
(式2) X&&&は正しい値ではX&&に縮退されます。
std::move(string("hello")) => std::move(string&&)
// At this point: the type of T is string
// remove_reference<T>::type for string
// The whole std::move is instantiated as follows
string&& move(string&& t) // t is the right value
{
return static_cast<string&&>(t); //return a right-valued reference
}
簡単に言うと、T&&を通過する右値は右値と同じ型のままで、T&&を通過する左値は通常の左参照となる。
static_cast<>を使用する際の注意点。明示的に定義された任意の型変換は、基礎となる const を含まない限り、static_cast を使用できます。
double d = 1;
void* p = &d;
double *dp = static_cast<double*> p; //correct
const char *cp = "hello";
char *q = static_cast<char*>(cp); //error: static cannot remove the const property
static_cast<string>(cp); //correct
remove_reference は、以下のコードでクラステンプレートを部分的に特殊化することで実装しています。
// The original, most generic version
template <typename T> struct remove_reference{
typedef T type; //define the type alias of T as type
};
//part of the version specialization that will be used for left-valued references and right-valued references
template <class T> struct remove_reference<T&> //left-valued reference
{ typedef T type; }
template <class T> struct remove_reference<T&&> //right-valued reference
{ typedef T type; }
// For example, the following three variables defined as a, b, and c are all int types
int i;
remove_refrence<decltype(42)>::type a; //use the original version
remove_refrence<decltype(i)>::type b; //left-valued reference to the special case version
remove_refrence<decltype(std::move(i))>::type b; //right-valued reference to the special case version
要約すると
std::move の実装は,まず,右値の参照を渡し,参照折りたたみの原理を利用して,T&& を通して右値を渡すことで型を変更しないか右値化し,T&& を通して左値は通常の左値参照となることでテンプレートが任意の実参照を渡して型を変更しないようにできるようにしています.そして,static_cast<>によって強制的に型変換を行い,T&&の右値参照を返し,static_cast<T>によって型変換できるのは,remove_reference<T&t;::型テンプレート ;, T&references によって T&& を取り除き具体型 T にするからです.
参考リンク
https://blog.csdn.net/fengbingchun/article/details/52558914
https://blog.csdn.net/cpriluke/article/details/79462388
関連
-
undefinederror: 'dynamic_cast' の前に unqualified-id を指定する必要があります。
-
std::logic_error' のインスタンスを投げた後に呼び出された実行エラー終了 what(): basic_string::_S_const
-
C++がpythonを呼び出す
-
C++のostreamの詳細な使用方法
-
error: label 'xxxxxxx' [-fpermissive] にジャンプします。
-
警告:組み込み関数 'malloc' の互換性のない暗黙の宣言を修正する方法
-
C++: エラー C2228: '.str' の左側にはクラス/構造体/結合が必要
-
C++: エラー C2280: 削除された関数を参照しようとしています。
-
[エラー]'cout' はこのスコープで宣言されていません。
-
ベクター使用時、ベクター添え字が範囲外、その他類似のエラーが発生する。
最新
-
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++ エラー: 'map' は型名ではありません。
-
エラー: 'xxx' は事前宣言と C++ ヘッダーファイルが互いに含まれているため、型名になりません。
-
C++ - 文字列クラス超詳細紹介
-
gcc/g++ コンパイル時のエラー解析で期待される型指定子の前に
-
警告を表示します。ISO C++は文字列定数を'char*'に変換することを禁じています[-Write-strings]。
-
C++ Error no matching function for call to 'std::basic_ofstream<char>::basic_ofstream(std::string&)
-
"エラー:不完全なクラス型へのポインタは許可されません。"の前方宣言。
-
抽象クラス型 "my class "のオブジェクトは使用できません 解決方法
-
C++ shared_ptr コンパイルエラー 'shared_ptr' がこのスコープで宣言されていない問題を修正しました。
-
ベクトル添え字が範囲外のコンテナの使用、その他類似のエラー