从 C++ 11 标准开始,被称为现代 C++。其中的一个原因,就是增加了 Lambda 表达式。
仿函数
在没有 Lambda 表达式之前,如果要在局部运行一个函数,只能使用仿函数。
仿函数就是在局部定义一个类,然后重载 ()
运算符:
class 类名称 {
public:
返回类型 operator() (参数列表) {函数体}
};
Lambdta 表达式
定义:
[外部变量捕获列表] (参数列表) mutable throw() -> 返回类型 {函数体}
- 捕获方式:
=
值传递,&
引用传递,后面可以接外部变量名,如果仅有符号则表示所有外部变量都使用这种捕获方式。 - 参数列表:和普通函数一样
- 返回类型:和普通函数一样
默认情况下,Lambda 表达式是 const 属性。加上 mutable
可以移除 const 属性。
throw()
表示 Lambda 里面可能会抛出异常,可以使用 noexcept
表示不会抛出任何异常。
以上除了方括号和大括号是必须的符号,其它部分都是可选的,所以一个极简的 Lambada 表达式可以这样写:
auto b = []{};
C++ 11/14 ,对于 this
只能按引用捕获,而不能按值捕获:
// 无论如何 this 总是按引用捕获,其它企图按值捕获的写法会导致编译报错
[this]() {
}
// 按引用捕获所有外部变量,this 也是隐式按引用捕获
[&]() {
}
// 按值捕获所有外部变量,this 是隐式按引用捕获
[=]() {
}
C++ 17 开始支持按值捕获 this
了:
// 按值捕获 this
[*this]() {
}
// 按值捕获所有外部变量,this 也是隐式按值捕获,这一点和 C++ 11/14 不一致
[=]() {
}
C++ 20 开始废弃 this 的隐式按值捕获,按值捕获时,需要显式指定 this 的捕获方式:
// 显示指定 this 是按引用捕获
[=, this]() {
}
// 显示指定 this 是按值捕获
[=, *this]() {
}
推荐任何时候都显示指定 this 的捕获方式,这样可以避免编译标准的差异带来隐藏的问题。
Lambda 表达式是一个匿名类的类型,可以理解为是仿函数的一个语法糖,如果要确切的表达一个 Lambda 表达式的类型,唯一的方式是使用 auto
:
auto block = [=, this](int a) -> int {
return a;
};
int r = block(123);
Lambda 表达式也可以传给函数指针类型或者 std::function
:
std::function<int(int)> f = [=, this](int a) -> int {
return a;
};
f(123);
// 初始化可以使用函数指针指向 Lambda 表达式
int (*pfn)(int) = [](int a) -> int {
return 10;
};
// 但是不能这样赋值
auto block = [](int a) -> int { return 10; };
int (*p)(int) = block;
可以把 Lambda 表达式传给参数为函数指针的函数:
// 函数 foo 的参数为函数指针类型
int foo(int (*p)(int)) {
return p(10);
}
// 可以把 lambda 表达式作为参数
foo([](int a){ return 10; });
参考资料
https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170
https://docs.microsoft.com/en-us/cpp/cpp/examples-of-lambda-expressions?view=msvc-170