本文讨论的是在C++ 11标准下使用Lambda表达式。
lambda表达式在python,C#等语言中运用都是十分广泛的。使用lambda表达式可以快速定义匿名的函数,方便使用且能防止非法调用。
lambda表达式在C++中的声明格式如下:
1. 传递方式可以为空,&,=,或者为每一个外部参数指明传递方式。= 表示为值传递方式,& 表示为引用传递方式。就算为空,中括号也不可以省略。
2. 参数列表即函数的参数,参数可以按值或者按引用传递。如果没有参数,可以省略(包括小括号)。
3. mutable是可选的,如果声明了mutable则可以修改按值传递进来的参数的拷贝(在lambda表达式外修改不可见)。
4. throw也是可选的,用来抛出函数处理过程中的异常,需制定抛出的异常的类型。
5. 返回类型是函数返回值的类型,在两种情况下可以省略,在后面叙述。
6. 函数体,实现功能的部分。
说了这么多,给一个实际的例子。将一个普通的函数转化为lambda表达式。
用函数声明写一个加法的实现:
int add(int x, int y)
{
return x + y;
}
用lambda表达式改写
auto add = [](int x, int y) { return x + y; };
这两种声明方法的结果是相同的,调用都是采用一般的函数调用方式。给个调用的例子。
int res = add(1, 2);
由于写的lambda表达式本身就可以作为函数,所以可以把上面的lambda表达式的声明和调用写在一起。
int res = [](int x, int y) { return x + y; } (1, 2);
从上面的例子中可以发现,在lambda表达式中,并未指明返回类型。这就是我们所说的两种特殊情况之一,函数体内部只有一句return,可以自动根据表达式中参数类型确定返回类型。另一种特殊情况是无返回或返回void,可以不写。其他情况都要指明返回类型。
如果指明返回类型,上面的lambda表达式这样写:
[](int x, int y) -> int { return x + y; }
当然,lambda表达式肯定不只是这样用的。再给一个例子,让数组a的每一个元素+1。
用for循环做的代码就不给了。只讲用lambda怎么写。
如果这个问题给一个python程序员,肯定会很快写出:
map(lambda x: x + 1, a)
如果让一个C#程序员做,也可以很快写出来:
a = a.Select(x => x + 1);
如果让一个C++程序员做,用lambda表达式就是这么写:
std::for_each(a, a+10, [](int &i) {i += 1;});
上面列举了三种语言对于lambda表达式的使用。可能相对于其他编程语言,C++中的写法较繁琐一些。但lambda的引入也能使得代码更加简洁。
对于刚才中括号里面为什么必须要加&,再给一个例子。
[a, &b](int i) {b = a + i; }(5);
这个例子中,a和b并不在函数体中定义,而是一个外部的参数。对于外部参数,需要指明捕获方式是按值还是引用传递。这个例子中是对于a按值传递,因为b要修改必须按引用传递。在上一个例子中,只写一个&而并没有写明参数的名称,表示对所有参数都执行按引用传递。这一点和C#等编程语言是不同的,C#中只有按引用传递。
最后给大家看一行C++代码
[](){}();
各种括号,看似很奇怪,其实很合理,这就是lambda的使用。
——————————
在C++11中,参数列表中的类型必须是确定类型,即只允许下面的写法
auto add = [](int x, int y) { return x + y; };
在C++14中,允许泛型lambda,即可以使用下面的写法
auto add = [](auto x, auto y) { return x + y; };
文中所涉及的所有代码均在Visual Studio 2015中通过。