lambda表达式
lambda表达式表示可调用的代码单元。它可以被认为是一个未命名的内联函数。与任何函数一样,lambda具有返回类型,参数列表和函数体。 与函数不同,lambda可以在函数内定义 。lambba表达式具有如下形式:
Code:
[capture list] (parameter list) -> return type { function body}
其中捕获列表是封闭函数中定义的局部变量(通常为空)列表; 返回类型,参数列表和函数体与任何普通函数相同。但是,与普通函数不同,lambda必须使用尾随返回来指定其返回类型。
我们可以省略参数列表和返回类型中的一个或两个,但必须始终包含捕获列表和函数体:
Code:
auto f = [] { return 42; };
在这里,我们将f
定义为一个不带参数的可调用对象,返回42。
我们通过使用调用运算符调用lambda的方式与调用函数的方式相同:
Code:
cout << f() << endl; // prints 42
省略lambda中的括号和参数列表等同于指定空参数列表。因此,当我们调用f时,参数列表为空。如果省略返回类型,则lambda具有推断的返回类型,该类型取决于函数体中的代码。如果函数体只是一个return
语句,则返回类型是从返回的表达式的类型推断出来的。否则,返回类型为void
。
注:对于具有函数体的lambda表达式,函数体中包含除了单个return
语句之外的任何其他语句,并且未指定返回类型的lambda表达式,一律将其返回类型设定为void
。
关于lambda表达式更多信息可参考lambda表达式。
lambda表达式的尾随返回类型
当我们要定义lambda表达式的返回类型时,必须显示指明,即利用尾随返回类型的声明方式。
举个简单的例子,我们可以使用标准库transform
算法和lambda表达式来将序列中的每个负值替换为其绝对值:
Code:
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) { return i < 0 ? -i : i; });
在这个调用中,我们给transform
传递了一个lambda,它返回其参数的绝对值。lambda主体是一个return
语句,它返回条件表达式的结果。我们不需要指定返回类型,因为可以从条件运算符的类型推断出该类型。
但是,如果我们使用if
语句编写看似等效的程序,我们的代码将无法编译:
Code:
// error: cannot deduce the return type for the lambda
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) { if (i < 0) return -i; else return i; });
编译器将这个版本的lambda的返回类型推断为void
,但我们需要其返回了一个值。
为使上述代码能正确工作,我们需要显示指明lambda表达式的返回类型。
Code:
transform(vi.begin(), vi.end(), vi.begin(),
[](int i) -> int
{ if (i < 0) return -i; else return i; });
在这种情况下,transform
函数的第四个参数是一个带有空捕获列表的lambda表达式,它接受int
类型的单个参数并返回int
类型的值。它的函数体是一个if
语句,它返回其参数的绝对值。
标准库函数bind
我们经常会遇到一类需要接收函数指针作为其参数的函数,这类函数在内部处理时会自动为函数指针传参。但是我们有时需要将这类函数外部的参数传给该函数指针并一起参与这类函数内部的运算。除了使用lambda表达式之外,我们还可以使用bind
函数来实现这种需求。
bind
函数定义在functional
头文件中。调用bind
的一般形式如下:
Code:
auto newCallable = bind(callable, arg_list);
其中newCallable
本身是一个可调用对象,arg_list
是一个以逗号分隔的参数列表,对应于给定callable
的参数。也就是说,当我们调用newCallable
时,newCallable
调用callable
,传递arg_list
中的参数。
arg_list
中的参数可以包含_n
形式的名称,其中n是整数。这些参数是表示newCallable
参数的“占位符”。它们代表“代替”将传递给newCallable
的参数。数字n是生成的callable
中参数的位置:_1
是newCallable
中的第一个参数,_2
是第二个参数,依此类推。
Code:
bool check_size(const string &s, string::size_type sz)
{
return s.size() >= sz;
}
// check6 is a callable object that takes one argument of type string
// and calls check_size on its given string and the value 6
auto check6 = bind(check_size, _1, 6);
string s = "hello";
bool b1 = check6(s); // check6(s) calls check_size(s, 6)
对bind
的调用只有一个占位符,这意味着check6
只接受一个参数。占位符出现在arg_list
中第一位,这意味着check6
中的参数对应于check_size
的第一个参数。该参数是一个const
型字符串的引用,这意味着check6
中的参数也是一个const
型字符串的引用。 因此,对check6
的调用必须传递string
类型的参数,check6
将其作为check_size
的第一个参数传递。
arg_list
中的第二个参数(即,bind
的第三个参数)是数值6。该值绑定到check_size
的第二个参数。每当我们调用check6
时,它都会将数值6传递给check_size
作为其第二个参数。
注:_n
占位符定义在命名空间placeholders
中;placeholders
定义在命名空间std
中。所以要使用这些占位符,必须提供两个命名空间的名称。
Code:
using std::placeholders::_1;
关于bind
函数更详细的信息请参考std::bind。
参考文献
[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.