右值引用
左值(lvalue, left value),顾名思义就是赋值符号左边的值。准确来说, 左值是表达式(不一定是赋值表达式)后依然存在的持久对象。
右值(rvalue, right value),右边的值,是指表达式结束后就不再存在的临时对象。
纯右值(prvalue, pure rvalue),纯粹的右值,要么是纯粹的字面量,例如 10, true; 要么是求值结果相当于字面量或匿名临时对象,例如 1+2。非引用返回的临时变量、运算表达式产生的临时变量、 原始字面量、Lambda 表达式都属于纯右值。
将亡值(xvalue, expiring value),是 C++11 为了引入右值引用而提出的概念(因此在传统 C++ 中, 纯右值和右值是同一个概念),也就是即将被销毁、却能够被移动的值。
概述:引用的本质是别名,可以通过别名修改变量的值,转参时传引用避免拷贝。
- 左值引用:指向左值,不能指向右值的引用。const左值引用可以指向右值。
int main() {
int a = 5; // a是个左值
int &ref_a_left = a;// 左值引用指向左值
const int &ref_left = 5; // const左值引用可以指向右值
cout << ref_a_left << endl; // 5
cout << a; // 5
}
- 右值引用:指向右值,不能指向左值。
int main() {
int &&lref = 5;
// int &&lref2 = lref;
// lref拥有名称,是有地址的,是左值。
}
-
被声明出来的左、右值应用都是左值。
-
函数参数:
#include <iostream>
using namespace std;
int change(int &&a) {
a = 10;
return a;
}
int change2(int &a) {
a = 10;
return a;
}
int change3(int a) {
a = 10;
return a;
}
int main() {
int &&rref = 5;
int ref = 5;
int &lref = ref;
// 测试右值引用
// cout<<change(rref)<<endl; 编译不通过,右值引用是左值
cout << change2(rref) << endl;
cout << change3(rref) << endl;
// cout << change(lref) << endl; 编译不通过,左值不能作为右值函数参数
cout << change2(lref) << endl;
cout << change3(lref) << endl;
// cout << change(ref) << endl; 编译不通过,左值不能作为右值函数参数
cout << change2(ref) << endl;
cout << change3(ref) << endl;
}
移动语义
概述:各种情况下对象的资源所有权转移问题。
解决的场景:
- 按值传入参数
- 如果使用const string& name作为参数,就会调用一次构造函数和移动构造函数。
class person {
public:
person (string name)
: name_(move(name))
{
}
string name_;
};
person p("Alice");
string bn = "Bob";
person p1(bn);
- 显式移动构造,将传入的字符串移入成员变量。
- 按值返回
vector<int> test() {
vector<int> v;
return v;
}
编译成:
std::vector<int, std::allocator<int> > test()
{
vector<int> v = std::vector<int, std::allocator<int> >() /* NRVO variable */;
return std::vector<int, std::allocator<int> >(static_cast<std::vector<int, std::allocator<int> > &&>(v));
}
- 接收右值表达式
优化表达式的值初始化对象或者给对象复制的性能。现在函数直接利用这些右值。
完美转发
std::forward成为完美转发,即经过转发的值的属性不变。没有完美转发的情况下,经过函数传参使得右值转为左值。
#include <iostream>
using namespace std;
void print(int &a) {
cout << "左值" << endl;
}
void print(int &&a) {
cout << "右值" << endl;
}
template<typename X>
void test(X &&x) {
print(x);
print(std::forward<X>(x));
print(std::move(x));
}
int main(int argc, char *argv[]) {
int a = 10;
test(a);
cout << "============" << endl;
test(10);
}
/**
左值
左值
右值
============
左值
右值
右值
**/