C++ 里的表达式有左值和右值的概念。
一个表达式(expression
)要么是广泛左值类型(glvalue
),要么是右值类型(rvalue
)。
广泛左值(glvalue
),还区分为左值(lvalue
)和将亡值(xvalue
)。
右值类型(rvalue
),也区分为纯右值(prvalue
)和将亡值(xvalue
)。
左值引用符:&
,右值引用符:&&
左值
可以引用到其地址的都是左值类型。
虽然字面量属于纯右值,但有唯一例外:字符串字面量是左值类型。
右值
不能通过地址对其进行访问的属于纯右值,比如字面量、函数返回非引用类型的值、由表达式生成的临时变量:
int a = 5; // 5 是字面量,是纯右值。变量 a 是左值
将亡值
a + 5; // 这个结果是将亡值
a << 16; // 这个结果是将亡值
struct Size {
float width;
float hight;
};
Size getSize() {
return Size { 800, 600 };
}
getSize(); // 这里返回的是将亡值
getSize().width = 900; // 编译错误:Expression is not assignable
引用
引用就是绑定关系,使表达式可以使用具体的名称来引用。
左值引用,就是一般的引用,相当于起了一个别名:
struct Size {
float width;
float hight;
};
void foo(Size &size) {
size.width = 800;
}
Size s { 100, 100 };
foo(s); // 传进来的是 s 的引用,在函数内部相当于直接操作 s 本身
右值引用,也是绑定关系,但和左值的绑定有一点区别:是把右值使用移动的方式绑定到变量上。
int &&a = 6; // 把 6 移动给变量 a
a += 1; // 没有问题
C++ 有一个规定:凡具名者皆是左值:
struct Size {
float width;
float hight;
};
// 函数返回类型是引用,避免发生拷贝
Size& resize(Size &&size) {
return size;
}
resize({ 800, 600 });
在 for_loop
循环中,使用 const auto &
或 auto &&
比 auto &
更具有普适性。
在使用循环枚举 STL 容器内容时,使用引用可以避免发生拷贝:
std::vector<struct Size> list {
{ 100, 100 },
{ 200, 200 },
{ 300, 300 }
};
// 每次都会发生拷贝
for (auto size: list) {
}
// 改为引用,避免发生拷贝
for (const auto &size: list) {
}
当 std::vector
存储的数据类型是 bool
时,它内部会使用位(bit
)来存储,而不是为每个元素创建空间,这样做是为了节约内存而且提高存取速度。
std::vector<bool> l { true, false, true };
// 编译报错:Non-const lvalue reference to type '__bit_reference<...>' cannot bind to a temporary of type '__bit_reference<...>'
for (auto &b: l) {
}
这个时候返回的是右值,准确的说是将亡值,使用 auto &
指向右值是不对的。
for (const auto &b: l) {
}
// 或者使用右值引用
for (auto &&b: l) {
}
因为右值引用可以绑定将亡值(xvalue
),并且不需要 const
限定符。
所以在循环中更通用的写法就诞生了:auto &&
。
(全文完)