什么是Lambda表达式

Lambda表达式是一种在被调用的位置作为参数传递给函数的位置定义匿名函数对象的方法。

Lambda表达式的基本语法如下:

1
[capture list](parameter list) -> return type {function body}

其中:

  • capture list是捕获列表,用于指定Lambda表达式内可访问的外部变量参数传递方式(传值还是引用)
    捕获列表可为空。
    默认捕获模式为&(引用类型)
  • parameter list是参数列表,和普通函数一样的使用方法。
    在C++14中使用auto关键字实现泛型返回值。
  • return type 返回值类型,同函数的返回值类型。需要使用“->”指出返回值类型,但也可以省略此时将由编译器推导。
    在C++14中使用auto关键字实现泛型返回值。
  • function body函数体用于表达表达式内的具体逻辑。
    在C++14中使用constexpr关键字实现编译期计算。

Lambda表达式的捕获方式

值捕获

值捕获在值改变时Lambda表达式不受影响。

1
2
3
4
5
int x = 10;
auto f = [x](int num) -> int {return x + num; };
std::cout << f(5) << std::endl; //输出:15
x = 20;
std::cout << f(5) << std::endl; //输出:15

引用捕获

引用捕获在引用的值改变时也将改变Lambda表达式中的值。

1
2
3
4
5
6
int x1 = 10;
int x2 = 10;
auto f = [x1,&x2](int num) -> int {return x1+x2+num; };
std::cout << f(5) << std::endl; //输出:25
x2 = 20;
std::cout << f(5) << std::endl; //输出:35

隐式捕获

当捕获列表比较长时,可以省略同一捕获类型的变量名称,使用=或&替代。

1
2
3
4
5
6
7
8
9
10
11
int x1 = 10;
int x2 = 10;
int x3 = 0;
auto f = [=,&x3,&x1](int num) -> int {return x1+x2+x3+num; };
//auto f = [&,x3,x1](int num) -> int {return x1+x2+x3+num; }; 合法的
//auto f = [&,&x3,x1](int num) -> int {return x1+x2+x3+num; }; 非法的
//auto f = [=,x3,&x1](int num) -> int {return x1+x2+x3+num; }; 非法的
std::cout << f(5) << std::endl; //输出:25
x2 = 20;
x3 = 40;
std::cout << f(5) << std::endl; //输出:65

初始化捕获 (C++14 引入的一种新的捕获方式)

在捕获列表中使用初始化表达式,从而在捕获列表中创建并初始化一个新的变量,而不是捕获一个已存在的变量。这种方式可以使用 auto 关键字来推导类型,也可以显式指定类型。这种方式可以用来捕获只移动的变量,或者捕获 this 指针的值。

1
2
3
4
5
6
7
int x1 = 10;
int x2 = 10;
int x3 = 0;
auto f = [xsum = x1+x2,x3](int num) -> int {return xsum+x3+num; };
std::cout << f(5) << std::endl; //输出:25
x3 = 40;
std::cout << f(5) << std::endl; //输出:25

Lambda表达式底层原理

仿函数

仿函数是指行为像函数的对象。通过重载()运算符可以实现仿函数。

Lambda表达式底层原理

Lambda底层通过仿函数类实现。编译器把Lambda转换为匿名类的对象,匿名类**重载了operator()**运算符,可以像函数一样调用。

例如:

1
2
auto addLambda = [](int a,int b){return a+b;};
cout<< addLambda(3,4);

转换成一个lambda对象:

1
2
3
4
5
6
7
struct __Lambda_add{
int operator() (int a,int b){
return a+b;
}
};

__Lambda__add addLambda;

对于需要捕获变量的Lambda表达式,则编译器会创建一个有成员变量的__Lambda类。

1
2
int x = 10;
auto lambda = [x](int y) { return x + y; };

转换后:

1
2
3
4
5
6
7
8
9
10
11
struct __Lambda {
int x; // 按值捕获

__Lambda(int _x) : x(_x) {}

int operator()(int y) const {
return x + y;
}
};

__Lambda lambda(x); // 构造时捕获 x 的值

C++14标准支持泛型Lambda表达式,底层是生成一个泛型的Lambda类。

1
auto lambda = [](auto a, auto b) { return a + b; };
1
2
3
4
5
6
struct __Lambda {
template<typename T, typename U>
auto operator()(T a, U b) const {
return a + b;
}
};

其他注意事项

  • Lambda是值类型,可拷贝、可移动。
1
2
3
4
auto l1 = [x]() { return x; };
auto l2 = l1; // 拷贝闭包对象

std::function<int()> f = l1; // 转换成 std::function,可能涉及类型擦除和动态分配