C++11:lambda表达式

1、介绍

lambda表达式常用于向函数传递谓词,有些函数的参数只能接受一元谓词(如find_if),但是如果我们需要向函数传递二元谓词,这时候可以使用lambda表达式(匿名函数),lambda表达式是重载了运算符[]的函数。

2、lambda表达式的使用

一个lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数。与函数相似,一个lambda表达式具有一个返回类型、一个参数列表和一个函数体。一个lambda表达式具有如下的形式:

[捕获参数](参数列表) -> 返回类型 {函数体}

lambda表达式必须使用尾置返回类型。

(1)简单的lambda表达式

auto func = [] {return 42;};

上式中忽略了参数列表和返回类型,但是必须包含捕获列表和函数体。

cout <<func() <<endl; //调用func的方式和普通函数类似,此式输出42.

(2)含有参数的lambda表达式

向lambda表达式传递参数的时候,和普通函数类似,但是lambda表达式不能含有默认值,因此传递参数的时候实参与形参一定相等。
完成isShorter函数的lambda表达式如下:

[] (const string& a, const string& b) { return a.size() < b.size(); }

空捕获列表表示lambda表达式不使用其所在函数的局部变量。上述的lambda表达式调用的时候会进行比较两个参数的长短,并返回bool值。

在stable_sort()函数中使用上述的lambda表达式:

stable_sort(words.begin(), words.end(), [] (const string& a, const string& b) { return a.size() < b.size(); });

这样stable_sort函数在排序的时候就会调用lambda表达式,按字符串长度从小到大排列。

注:一个lambda表达式如果如果忽略返回类型,那么lambda表达式会根据函数体的返回值推断类型,但是前提是函数体只有一个return语句;如果lambda表达式忽略了返回类型,并且函数体不有return语句之外的语句,那么返回类型是void。

(3)使用捕获列表的lambda表达式

lambda表示一般是用于函数中,如果lambda表达式需要使用到函数中的局部变量,则需要是用捕获列表进行捕获。
如:使用find_if函数查找第一个满足字符串长度大于len的字符。

int len = 5;
stable_sort(words.begin(), words.end(), [] (const string& a, const string& b) { return a.size() < b.size(); });
find_if(words.begin(), words.end(), [len](const string& a) { return a.size() > len; });

上述例子中find_if函数中的第三个参数传递了一个lambda表达式,该表达式使用捕获列表捕获了参数len,对words中的每一个元素的长度和len进行比较,返回第一个长度大于len的字符串的迭代器。

(4)值捕获与引用捕获

值捕获:

lambda表达式采用[name]的形式捕获参数的时候,便是使用的值捕获。采用值捕获的前提是参数能够拷贝。与函数参数不同,被捕获的变量的值是在lambda表达式创建的时候拷贝的,而不是调用时拷贝。

int i = 40;
auto func = [i] {return i;};
i = 0;
auto j = func();  //此时变量j=40,func保存了我们创建它时i的拷贝
引用捕获:

引用捕获和变量的引用类似,在lambda表达式中采用引用捕获,那么在lambda表达式使用的便是引用所绑定的对象的值。如果采用引用捕获,我们必须确保被引用的对象在lambda表达式执行的时候是存在的。

int i = 40;
auto func = [&i] {return i;};
i = 0;
auto j = func();  //此时变量j=0,func保存的是i的引用,没有拷贝。
隐式捕获:

可以用=和&表示lambda表达式是采用值捕获还是引用捕获

find_if(words.begin(), words.end(), [=](const string& a) { return a.size() > len; });

使用=号表示采用的是值捕获(lambda表达式中所有的参数都是)。

find_if(words.begin(), words.end(), [&](const string& a) { return a.size() > len; });

使用&号表示采用的是引用捕获(lambda表达式中所有的参数都是)。

显式和隐式捕获混用:
for_each(words.begin(),  words.end(), [&, c] (const string &a) { os<< a << c; });

上面的lambda表达式中显式声明变量c采用值捕获的方式,其余变量采用引用捕获。

for_each(words.begin(),  words.end(), [=, &os] (const string &a) { os<< a << c; });

上面的lambda表达式中显式声明变量os采用引用捕获的方式,其余变量采用值捕获。

(5)指定lambda表达式的返回类型

如前所述,如果要指定lambda表达式的返回值类型,必须使用尾置返回类型。

transform(v.begin(),  v.end(), v.begin(),  [](int i) { return i < 0 ? i : -i; });

上面的表达式是将数组v中的负数替换为绝对值,因为lambda表达式中只有一个return 语句,因此可以不指定返回类型,编译器会自动推断返回类型为int。

但是若修改成:

transform(v.begin(), v.end(), v.begin(),  [](int i) { if(i < 0)  return -i; else return i;});

则会发生编译报错。需要采用尾置返回类型修改。

transform(v.begin(), v.end(), v.begin(),  [] (int i)  -> int { if(i < 0)  return -i; else return i;});

这样便指定了lambda表达式的返回类型。

总结:

(1)lambda表达式为匿名函数,常用于在函数中传递谓词,如排序算法中传递是递增排序还是递减排序。
(2)lambda表达式采用引用捕获参数的时候一定要保证在lambda表达式调用参数的时候变量一直存在。
(3)lambda表达式中如果只有一条return语句,返回类型由编译器自动推断;否则默认返回void
(4)要指定lambda表达式的返回类型要用尾置返回类型。
(5)lambda表达式要尽量短!(类似内联函数)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容