-
右值分为纯右值和将亡值:
纯右值(prvalue):代表计算结果的值,通常是临时对象或字面值。它不与特定内存位置绑定,在表达式求值后就会被销毁。
将亡值(xvalue):代表即将失效的对象,通常与对象的移动语义相关联。将亡值仍然与特定内存位置绑定,但这个位置即将失效,可能因为资源转移或即将销毁。
将亡值用来触发移动构造或移动赋值运算符,并进行资源转移,之后将调用析构函数;
从而避免了资源的重新分配 实现了移动语义。
将亡值示例:
#include <iostream>
#include <utility>
#include <string>
int main() {
std::string s1 = "Hello, World!";
std::string s2 = std::move(s1); // std::move(s1) 返回一个将亡值,并且std::move(s1)执行完之后s1会被销毁
std::cout << "s2: " << s2 << std::endl; // 输出: s2: Hello, World!
std::cout << "s1: " << s1 << std::endl; // s1 的内容是未定义的,但它仍然存在
return 0;
}
左值还是右值是表达式的属性,和数据类型不是同一个概念:
右值引用类型的变量的属性是左值,只要是变量都是左值。
对于形参是右值引用的函数,如: void process(int&& x),调用时候重点关注的是实参的属性是不是右值,而不是实参的数据类型。
通过下面的代码理解:
#include <iostream>
#include <utility>
// 一个接受左值引用的函数
void process(int& x) {
std::cout << "Lvalue reference called with: " << x << std::endl;
}
// 一个接受右值引用的函数
void process(int&& x) {
std::cout << "Rvalue reference called with: " << x << std::endl;
}
通用的包装函数,使用完美转发
template <typename T>
void wrapper(T&& arg) {
process(std::forward<T>(arg)); // 使用 std::forward 完美转发参数
}
int main() {
int a = 5;
int& a1 = a;
int&& b = 100;
wrapper(a); //Lvalue reference called with: 5
wrapper(10);//Rvalue reference called with: 10
wrapper(static_cast<int &>(a1));//Lvalue reference called with: 5
wrapper(b);//Lvalue reference called with: 100
wrapper(static_cast<int&&>(b));//Rvalue reference called with: 100
wrapper(std::forward<int&&>(b)); //Rvalue reference called with : 100
return 0;
}