右值
ISO/IEC 14882:2011
3.10 对表达式的分类如下所示
expression
/ \
glvalue rvalue
/ \ / \
lvalue xvalue prvalue
*Figure 1 — Expression category taxonomy*
lvalue
左值, 就是“能找到具体位置并可以被引用的值”
// 可以取地址
int a = 10;
int* p = &a; // 这里 a 是左值,因为你可以取它的地址。
// 可以出现在赋值运算符左边
a = 20; // 这里 a 是左值,因为它可以被赋值。
// 通常是变量或表达式的结果
int b = *p; // *p 是左值,指向了变量 a。
xvalue
亡值(“eXpiring value”), 是一个临时对象 或 被 std::move
标记的对象
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1); // v1 是亡值。
std::string temp = std::string("Temporary object"); // std::string("Temporary object") 是一个临时对象,在被复制或移动到 temp 后,原始临时对象会销毁。
glvalue
广义左值(“generalized” lvalue), 包含 lvalue
和 xvalue
prvalue
纯右值(“pure” rvalue), 通常是表达式的结果或者字面值,不依赖任何具体的内存地址。
int x = 42; // 字面值, 42 是纯右值。
int c = a + b; // 临时计算结果, a + b 是纯右值。
std::string func() {
return "hello"; // 非引用返回值, 返回的是纯右值。
}
rvalue
右值, 包含 xvalue
和 prvalue
右值引用
C++11 引入了一种新的引用,称为 右值引用。右值引用 T(非模板类型参数,如 int 或用户定义的类型)可以通过语法 T&& 创建。右值引用只绑定到右值。
int&& x = 0; // `x` 是右值引用 —— 绑定到右值临时变量 0)
转发引用(Forwarding references)
转发引用
在保持参数值类别的情况下传递参数(例如,左值仍作为左值传递,临时对象作为右值转发)。
转发引用允许引用根据类型绑定到左值或右值。转发引用遵循引用折叠的规则:
T& &
折叠为T&
T& &&
折叠为T&
T&& &
折叠为T&
T&& &&
折叠为T&&
#include <iostream>
#include <string_view>
#include <type_traits>
template <typename T>
constexpr std::string_view type_name() {
#if defined(__clang__) || defined(__GNUC__)
return __PRETTY_FUNCTION__;
#elif defined(_MSC_VER)
return __FUNCSIG__;
#else
return "Unsupported compiler";
#endif
}
template <typename T>
void test1(T& param) {
std::cout << "param = " << type_name<decltype(param)>() << "\n";
}
template <typename T>
void test2(T&& param) {
std::cout << "param = " << type_name<decltype(param)>() << "\n";
}
int main() {
int x = 42;
test1<int&>(x); // T& & -> T&
test2<int&>(x); // T& && -> T&
test1<int&&>(x); // T&& & -> T&
test2<int&&>(42); // T&& && -> T&&
return 0;
}