C++ lamda表达式

C++11开始支持lamda,也就是匿名函数,格式如下:
[capture](parameters) -> return_type { function_body }

下面是一个简单的lamda表达式例子:
[](int x, int y) -> int { return x + y; }

C++11也支持闭包。闭包在声明lamda表达式的时候被定义,使用中括号[]括起来。这个机制允许变量按值或者按引用被捕获。

[]        //没有定义变量。如果尝试使用外部变量会报错。
[x, &y]   //x按值捕获,y按引用捕获
[&]       //任意外部变量都是隐式地按引用捕获
[=]       //任意外部变量都是隐式地按值捕获
[&, x]    //x显式按值捕获.其余变量按引用捕获
[=, &z]   //z显示按引用捕获,其余变量按值捕获

按值捕获的变量默认都是constant常量,在参数列表后添加mutable能使其non-constant。

int x = 100;
auto f = [x]() mutable { ++x; }; //x是值传递,但是可以这样写 
f();

接下来两个例子展示了lamda表达式的用法:

std::vector<int> some_list{ 1, 2, 3, 4, 5 };
int total = 0;
std::for_each(begin(some_list), end(some_list), 
              [&total](int x) {	total += x; });

这个例子计算了这个list中所有元素的合。变量total被存为lamda函数闭包的一部分。由于它是栈变量(stack variable),函数能改变它的值。

std::vector<int> some_list{ 1, 2, 3, 4, 5 };
int total = 0;
int value = 5;
std::for_each(begin(some_list), end(some_list), 
             [&total, value, this](int x) { total += x * value * this->some_func(); });

这个例子当中,total按照引用来捕获,但是value是按值的,传递的是一个copy。
对this的捕获比较特殊, 它只能按值捕获. this只有当包含它的最靠近它的函数不是静态成员函数时才能被捕获.对protect和private成员来说, 这个lambda函数与创建它的成员函数有相同的访问控制. 如果this被捕获了,不管是显式还隐式的,那么它的类的作用域对Lambda函数就是可见的. 访问this的成员不必使用this->语法,可以直接访问.
不同编译器的具体实现可以有所不同,但期望的结果是:按引用捕获的任何变量,lambda函数实际存储的应该是这些变量在创建这个lambda函数的函数的栈指针,而不是lambda函数本身栈变量的引用. 不管怎样, 因为大数lambda函数都很小且在局部作用中, 与候选的内联函数很类似, 所以按引用捕获的那些变量不需要额外的存储空间.
如果一个闭包含有局部变量的引用,在超出创建它的作用域之外的地方被使用的话,这种行为是未定义的!
lambda函数是一个依赖于实现的函数对象类型,这个类型的名字只有编译器知道. 如果用户想把lambda函数做为一个参数来传递, 那么形参的类型必须是模板类型或者必须能创建一个std::function类似的对象去捕获lambda函数.使用auto关键字可以帮助存储lambda函数

auto my_lambda_func = [&](int x) { /*...*/ };
auto my_onheap_lambda_func = new auto([=](int x) { /*...*/ });

这里有一个例子, 把匿名函数存储在变量,数组或vector中,并把它们当做命名参数来传递

#include <functional>
#include <iostream>
#include <vector>

double eval(std::function<double(double)> f, double x = 2.0) {
  return f(x);
}

int main() {
  std::function<double(double)> f0 = [](double x) { return 1; };
  auto f1 = [](double x) { return x; };
  decltype(f0) fa[3] = {f0, f1, [](double x) { return x * x; }};
  std::vector<decltype(f0)> fv = {f0, f1};
  fv.push_back([](double x) { return x * x; });
  for (size_t i = 0; i < fv.size(); i++) {
    std::cout << fv[i](2.0) << std::endl;
  }
  for (size_t i = 0; i < 3; i++) {
    std::cout << fa[i](2.0) << std::endl;
  }
  for (auto& f : fv) {
    std::cout << f(2.0) << std::endl;
  }
  for (auto& f : fa) {
    std::cout << f(2.0) << std::endl;
  }
  std::cout << eval(f0) << std::endl;
  std::cout << eval(f1) << std::endl;
  std::cout << eval([](double x) { return x * x; }) << std::endl;
}

一个没有指定任何捕获的lambda函数,可以显式转换成一个具有相同声明形式函数指针.所以,像下面这样做是合法的:

auto a_lambda_func = [](int x) { /*...*/ };
void (* func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.
posted @ 2020-03-10 18:03  肚子好疼呀  Views(324)  Comments(0Edit  收藏  举报