一、forward
作用:完美转发,参数在传递过程中,保持原有的属性。即参数原来是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值。
在学习forward之前,我们先看一个例子:
int &&a = 5; // a = 50; // 修改成功 int &&right_ref = a; // 错误
分析:这里a是一个右值引用,指向右值5。 但是,a本身是个左值(如果这里不理解,复习下我之前的文章左值右值)。用右值引用指向左值是不对的。那么,如何保持a自身右值引用的特性呢?forward就是干这个的。
int &&a = 5; // a = 50; // 修改成功 int &&right_ref = std::forward<int>(a); // ok
下面我们再通过例子来看下:
#include <iostream> using namespace std; template <class T> void Print(T &t) { cout << "L" << t << endl; } template <class T> void Print(T &&t) { cout << "R" << t << endl; } template <class T> void func(T &&t) { Print(t); Print(std::move(t)); Print(std::forward<T>(t)); } int main() { cout << "-- func(1)" << endl; func(1); cout << "-- func(x)" << endl; int x = 10; func(x); // x本身是左值 cout << "-- func(std::forward<int>(y))" << endl; int y = 20; func(std::forward<int>(y)); // return 0; }
运行结果:
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/1462219cabb54488ae735871b91ad771.png
二、匿名函数Lambda
2.1 定义
lambda表达式可以看成是一般函数的函数名被略去,返回值使用了一个 -> 的形式表示。唯一与普通函数不同的是增加了“捕获列表”。
2.2 语法格式
int main() { [捕获列表](参数列表) mutable(可选) 异常属性 -> 返回类型 { // 函数体 } auto Add = [](int a, int b)->int { return a + b; }; std::cout << Add(1, 2) << std::endl; //输出3 return 0; }
2.3 捕获列表
有时候需要在匿名函数体内部使用外部变量,lambda表达式使用捕获列表来传递参数,根据传参的行为,分为:
2.3.1 值捕获
与参数传递类似,值捕获的前提是变量可以拷贝,不同点是,拷贝发生在lambda表达式被创建时,而非调用时。匿名函数体内部不能修改外部变量的值。
void func() { cout << "func" << endl; int c = 12; int d = 30; // 创建时就已经把c、d的值拷贝进去了,所以输出:d = 30; auto Add = [c, d](int a, int b)->int { // c = 100; // 编译报错 cout << "d = " << d << endl; return c; }; d = 20; std::cout << Add(1, 2) << std::endl; }
2.3.2 引用捕获
匿名函数体内部可以修改外部变量。
void func() { cout << "func" << endl; int c = 12; int d = 30; // 创建时就已经把c、d的值拷贝进去了 auto Add = [&c, &d](int a, int b)->int { c = 100; // 编译ok cout << "d = " << d << endl; return c; }; d = 20; // 因为是引用捕获,且这里修改了d的值,所以输出:20 std::cout << Add(1, 2) << std::endl; }
2.3.3 隐式捕获
手动书写捕获列表,有时候会很复杂。我们可以交给编译器去完成,在捕获列表中写一个 & 或者 = ,向编译器声明是引用捕获还是值捕获。
auto Add = [&](int a, int b)->int { c = 100; // 编译ok cout << "d = " << d << endl; return c; };
2.3.4 [] 空捕获列表
表明不能使用外部变量。
auto Add = [](int a, int b)->int { c = 100; // 编译报错 cout << "d = " << d << endl; return c; // 编译报错 };
2.3.5 表达式捕获
上面4种,值和引用捕获都是捕获的已经在外部作用域声明的变量,因此这些捕获的方式均为左值。c++14之后,支持捕获右值,允许捕获的成员用任意的表达式进行初始化。
// C++14 支持表达式捕获 void func() { cout << "func" << endl; auto important = std::make_unique<int>(1); auto add = [v1 = 1, v2 = std::move(important)](int x, int y) -> int { return x + y + v1 + (*v2); }; std::cout << add(3,4) << std::endl; }
2.3.6 泛型Lambda
在c++14之前,Lambda表达式的形参必须执行具体的类型。从c++14开始,支持auto泛型形参。
//泛型 Lambda C++14 void func() { cout << "func" << endl; auto add = [](auto x, auto y) { return x+y; }; std::cout << add(1, 2) << std::endl; std::cout << add(1.1, 1.2) << std::endl; }
2.3.7 可变Lambda
- 采用值捕获的方式,lambda不能修改其值,如果想要修改,使用mutable修饰;
- 采用引用捕获的方式,lambda可以直接修改其值
文章参考与<零声教育>的C/C++linux服务期高级架构。