匿名函数
示例 > C++ > C++ 11

C++11标准提供了匿名函數的支持，在《ISO/IEC 14882:2011》（C++11标准文档）中叫做lambda表達式。一個lambda表達式有如下的形式：
[capture] (parameters) mutable exception attribute -> return_type { body }
必须用方括号括起来的capture列表来开始一个lambda表达式的定义。
lambda函数的形参表比普通函数的形参表多了3条限制：
参数不能有缺省值
不能有可变长参数列表
不能有无名参数
如果lambda函数沒有形參且没有mutable、exception或attribute声明，那麼参数的空圆括號可以省略。但如果需要给出mutable、exception或attribute声明，那么参数即使为空，圆括号也不能省略。
如果函數體只有一個return語句，或者返回值類型為void，那麼返回值類型声明可以被省略：
[capture](parameters){body}
一個lambda函數的例子如下：
[](int x, int y) { return x + y; } // 從return語句中隱式獲得的返回值類型
[](int& x) { ++x; }   // 沒有return語句 -> lambda函數的返回值為void
[]() { ++global_x; }  // 沒有參數，僅僅是訪問一個全局變量
[]{ ++global_x; }     // 與前者相同，()可以被省略
在上面的第一個例子中這個無名函數的返回值是decltype(x+y)。如果lambda函數體的形式是return expression，或者甚麼也没返回，或者所有返回語句用decltype都能檢測到同一類型，那麼返回值類型可以被省略。
返回值類型可以显式指定，如下所示：
[](int x, int y) -> int { int z = x + y; return z; }
在這個例子中，一個臨時變量，z，被創建來儲存中間過程。與一般的函數一樣，中間值在調用的前後並不存在。甚麼也沒有返回的lambda表達式無需顯式指定返回值，沒有必要寫-> void代碼。
lambda函數可以捕获lambda函數外的具有automatic storage duration的变量，即函数的局部变量与函数形参变量。因而，具有static storage duration的变量不能被lambda捕获，只能直接使用，这包括静态局部变量。函数体与這些變量的集合合起来称做閉包。这些外部变量在聲明lambda表達式時列在在方括號[和]中。空的方括号表示没有外界变量被capture或者按照默认方式捕获外界变量。這些變量被傳值捕獲或者引用捕獲。对于传值捕获的变量，默认为只读（这是由于lambda表达式生成的为一个函数对象，它的operator()成员缺省有const属性）。修改这些傳值捕獲变量将导致编译报错。但在lambda表达式的参数表的圆括号后面使用mutable关键字，就允许lambda函数体内的语句修改傳值捕獲变量，这些修改与lambda表达式（实际上是用函数对象实现）有相同的生命期，但不影响被传值捕获的外部变量的值。lambda函数可以直接使用具有static存储期的变量。如果在lambda函数的捕获列表中给出了static存储期的变量，编译时会给出警告，仍然按照lambda函数直接使用这些外部变量来处理。因此具有static存储期的变量即使被声明为传值捕获，修改该变量实际上直接修改了这些外部变量。编译器生成lambda函數对应的函数对象时，不会用函数对象的数据成员来保持被“捕获”的static存储期的变量。示例：
[]        // 沒有定義任何變量，但必须列出空的方括号。在Lambda表達式中嘗試使用任何外部變量都會導致編譯錯誤。
[x, &y]   // x是按值傳遞，y是按引用傳遞
[&]       // 任何被使用到的外部變量都按引用傳入。
[=]       // 任何被使用到的外部變量都按值傳入。
[&, x]    // x按值傳入。其它變量按引用傳入。
[=, &z]   // z按引用傳入。其它變量按值傳入。
下面這個例子展示了lambda表達式的使用：
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; }
              );
在类的非静态成员函数中定义的lambda表达式可以显式或隐式捕捉this指针，从而可以引用所在类对象的数据成员与函数成员。对象的this指针必需显式捕获声明。因此，被捕获的类的数据成员总是用this指针来访问，如果this所指的对象不存在，则this是空悬指针。解决办法是在定义lambda之前用局部变量复制一份数据成员的值，然后捕获这个局部变量的值。
lambda函数的函数体中，可以访问下述变量：
函数参数
局部声明的变量
类数据成员：要求lambda表达式声明在类成员函数中，对象的this指针必需显式捕获声明。
具有静态存储期的变量（如全局变量）。一般情况下，lambda是用来捕获局部变量的，如果用其来捕获全局变量或者静态变量，那么编译器会报warning
被捕获的外部变量
显式捕获的变量
隐式捕获的变量，使用默认捕获模式（传值或引用）来访问。
lambda函数的数据类型是函数对象，保存时必须用std::function模板类型或auto关键字。
例如：
#include <functional>
#include <vector>
#include <iostream>

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(int i=0;i<fv.size();i++)
		std::cout << fv[i](2.0) << std::endl;
	for(int 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;
	return 0;
}
一个lambda函数的捕捉表达式为空，则可以用普通函数指针存储或调用。例如：
auto a_lambda_func = [](int x) { /*...*/ };
void (* func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.