C++ 中的仿函数(Functor)和匿名函数(Lambda)

Jun 27, 2022 • 预计阅读时间 2 分钟

从 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

LambdaFunctorC++
版权声明:如果转发请带上本文链接和注明来源。

lvv.me

iOS/macOS Developer

SwiftUI 学习资源

免费的自建 Git 服务