目录
1. 右值引用和移动语义
2. lambda表达式
1. 右值引用和移动语义
1.1 左值引用和右值引用
简单点说能取地址的是左值,不能取地址的是右值。
左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值(非const),左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
int main()
{// 以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;// 以下几个是对上面左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;return 0;
}
int main()
{double x = 1.1, y = 2.2;// 以下几个都是常见的右值10;x + y;fmin(x, y); string();// 以下几个都是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);// 这里编译会报错:error C2106: “=”: 左操作数必须为左值10 = 1;x + y = 1;fmin(x, y) = 1;return 0;
}
1.2 左值引用与右值引用比较
1. 左值引用只能引用左值,不能引用右值。
2. 但是const左值引用既可引用左值,也可引用右值。
int main()
{int b = 0;int& a = b;const int& c = b;const int& d = 10;return 0;
}
int main()
{int a = 10;int&& b = 10;int&& c = move(a);return 0;
}
1.3 右值引用使用场景和意义
先来看看左值引用的常见场景:引用做返回值和传参效率高,无拷贝,但引用返回局部对象,局部对象销毁就有问题,但是传值返回又会拷贝效率低,右值引用则是解决这方面的问题。
先来看看传统的传值返回:
上图调用了2次拷贝构造,但编译器会优化成直接一次拷贝构造。

1.4 完美转发
1. 在函数模板中 && 被称作万能引用或者引用折叠,既能接收左值也能接收右值。
2. 一但接收参数如果是右值,属性则会退化成左值(为了后续的资源交换),而后续在调用的时候就当作从左值调用了。
3. 完美转发(forward<T>)是用来在参数传递的时候保留原始类型的属性。
1.5 默认成员函数(移动构造和移动赋值)



2. lambda表达式
1. 如何对一组数据进行排序,这里借助sort()进行排序。
默认情况下sort是按照升序排序。可以传仿函数进行控制。
那么有没有更间接的方式写呢?答案是有的。
1.2 lambda表达式(匿名函数对象)
lambda表达式书写格式:[capture-list] (parameters) mutable -> return-type { statement }
1. 捕捉列表 -> [capture-list](不可省略):
class A
{
public:void fun(){// this指针传递auto it = [this] {};}
};
int main()
{int a = 10, b = 20;// 指定值传递auto swap1 = [a, b] {cout << a << " " << b << endl;};swap1();// 局部值传递auto swap2 = [=] {cout << a << " " << b << endl;};swap2();// 指定引用传递auto swap3 = [&a, &b]{int tmp = a;a = b;b = tmp;};swap3();cout << a << " " << b << endl;// 局部引用传递auto swap4 = [&]{int tmp = a;a = b;b = tmp;};swap4();cout << a << " " << b << endl;return 0;
}
int main()
{int x = 10;// 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。auto it1 = [=, &x] {};auto it2 = [&, x] {};// 捕捉列表不允许变量重复传递,否则就会导致编译错误。auto it3 = [=, x] {};auto it4 = [&, &x] {};return 0;
}
2. 参数列表 ->(parameters)(可省略):
int main()
{// 最简单的 lambda 表达式auto it = [] {};return 0;
}
3. 取消其常量性 -> mutable((可省略)):默认情况下,lambda函数总是一个const函数, 。使用该修饰符时,参数列表不可省略(即使参数为空)。
int main()
{int x = 10, y = 20;// 默认lambda函数是一个const函数auto swap1 = [x, y](){int tmp = x;x = y;y = tmp;};// mutable可以取消其常量性auto swap2 = [x,y]()mutable{int tmp = x;x = y;y = tmp;};return 0;
}
4. 返回值类型 ->return type(可省略):用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
int main()
{int x = 10;auto it1 = [&]()->int {return x;};// 自动推演返回值auto it2 = [&]() {return x;};auto it3 = [&]() {return false;};return 0;
}