std::move と std::forward を説明する前に、まず C++ の左辺値と右辺値の概念を理解する必要があります。
1. 左辺値、右辺値
(1) 左辺値: 一般に、メモリ内に対応する記憶単位を持つ値を指します。最も一般的なものは、プログラム内で作成される変数です。
(2) 右辺値: 一般に、定数や式計算用の一時変数など、対応する記憶単位 (レジスタ内の即値、中間結果など) を持たない値を指します。
式が左辺値であるか右辺値であるかを判断するには、次のようにします。
(1) = の左側に配置できる式は左辺値、逆に = の右側にのみ配置できる式は右辺値です。
// 其中x是一个左值,字面值5是一个右值
int x = 5;
// 错误,5 不能为左值
5 = x;
説明: C++ の左辺値は右辺値としても使用できます。
// y 是一个左值
int y = 10;
// x、y 都是左值,但可以将 y 可以当做右值使用
x = y;
(2) 格納アドレスを取得できる名前付きの式は左辺値、それ以外の場合は右辺値です。
上記の例では、変数 x と y は変数名であり、それらの格納アドレスは &x と &y を通じて取得できるため、x と y は両方とも左辺値です; 逆に、リテラル 5 と 10 には名前も格納アドレスもありません。したがって、5 と 10 は右辺値です。
2. 左辺値参照、右辺値参照
(1) 左辺値参照: C++ では & を使用して変数を参照します。私たちがよく知っている参照は左辺値参照です。
int num = 10;
int& b = num; //正确
int& c = 10; //错误
const int& c = 10; //正确
(2) 右辺値参照:
右辺値自体には対応する記憶単位がないため、参照できません。右辺値参照は、実際には論理的な概念にすぎません。最大の機能は、左辺値を右辺値に似たものにすることです(次のプログラム例)。これにより、変数間の転送が「セマンティック転送」に沿って行われ、転送のオーバーヘッドが削減されます。間の複数のコピー。右辺値の参照記号は && です。
注:
(1) 右辺値参照は左辺値参照をサポートしません;
(2) 非定数右辺値参照は非定数右辺値のみを参照できます;
(3) 定数右辺値は定数右辺値と非定数右辺値を参照できます。
int num1 = 10;
const int num2 = 100;
int&& a = num1; //编译失败,非常量右值引用不支持引用非常量左值
int&& b = num2; //编译失败,非常量右值引用不支持引用常量左值
int&& c = 10; //编译成功,非常量右值引用支持引用非常量右值
const int&& d = num1; //编译失败,常量右值引用不支持引用非常量左值
const int&& e = num2; //编译失败,常量右值引用不支持引用常量左值
const int&& f = 100; //编译成功,常量右值引用支持引用右值
3. move()関数
move() 関数は、左辺値を右辺値に強制します。しかし、move() には移動する機能がありません。
int num = 10;
int&& a = std::move(num); //编译成功
std::cout << a << std::endl; //输出结果为10;
std::cout << num << std::endl; //输出结果为10;move只是把左值强制转换成右值,并没有移动能力。
a = 20;
std::cout << num << std::endl; //输出20
move() の印象を深めるために、別の例を見てみましょう。
std::string str1 = "I love C++";
std::string str2 = std::move(str1);
std::cout << "str2:" << str2 << std::endl;
std::cout << "str1:" << str1 << std::endl;
結果:
move() 関数が実行された後、str1 の内容がなくなっていることがわかりますが、削除されたのでしょうか? move() には移動する機能がないと前述しましたが、str1 の内容が空である理由は文字列の「移動コンストラクター」です。別の例を見てみましょう。
std::string str3 = "I love C";
std::string&& def = std::move(str3);
std::cout << "str3:" << str3 << std::endl;
std::cout << "def:" << def << std::endl;
結果:
4. forward() 関数
std::forward の役割は完全転送であり、左辺値が渡された場合は左辺値参照となり、右辺値が渡された場合は右辺値参照となります。
std::move は、不必要なコピーのオーバーヘッドを削減し、プログラムの効率を向上させることができます。しかし、 std::forward の役割は転送であり、左辺値参照は左辺値参照、右辺値参照、または右辺値参照に転送されますが、その意味は何でしょうか?
プログラムの実行中に、実際には参照の受け渡しのための追加の暗黙的な変換が行われることが判明しました。右辺値参照パラメータは、関数呼び出しによって転送された後に左辺値参照に変換される可能性がありますが、これは私たちが望んでいることではありません。の結果を見るために。
例:
#include <iostream>
template<typename T>
void print(T& t) {
std::cout << "左值" << std::endl;
}
template<typename T>
void print(T&& t) {
std::cout << "右值" << std::endl;
}
template<typename T>
void testForward(T&& v) {
print(v);
print(std::forward<T>(v));
print(std::move(v));
}
int main(int argc, char* argv[])
{
testForward(1); // 传入右值
std::cout << "======================" << std::endl;
int x = 1;
testForward(x); // 传入左值
}
結果:
参考:
(1) 【C++の機能】std::move、std::forwardの理解