#include<algorithm>#include<array>#include<iostream>#include<string_view>// Our function will return true if the element matchesboolcontainsNut(std::string_viewstr){// std::string_view::find returns std::string_view::npos if it doesn't find// the substring. Otherwise it returns the index where the substring occurs// in str.return(str.find("nut")!=std::string_view::npos);}intmain(){std::array<std::string_view,4>arr{"apple","banana","walnut","lemon"};// Scan our array to see if any elements contain the "nut" substringautofound{std::find_if(arr.begin(),arr.end(),containsNut)};if(found==arr.end()){std::cout<<"No nuts\n";}else{std::cout<<"Found "<<*found<<'\n';}return0;}
#include<algorithm>#include<array>#include<iostream>#include<string_view>intmain(){constexprstd::array<std::string_view,4>arr{"apple","banana","walnut","lemon"};// Define the function right where we use it.constautofound{std::find_if(arr.begin(),arr.end(),[](std::string_viewstr)// 匿名函数,无闭包{return(str.find("nut")!=std::string_view::npos);})};if(found==arr.end()){std::cout<<"No nuts\n";}else{std::cout<<"Found "<<*found<<'\n';}return0;}
// Bad: We have to read the lambda to understand what's happening.returnstd::all_of(array.begin(),array.end(),[](inti){return((i%2)==0);});
可以通过下面的方式提升可读性。
123456789
// Good: Instead, we can store the lambda in a named variable and pass it to the function.autoisEven{[](inti){return((i%2)==0);}};returnstd::all_of(array.begin(),array.end(),isEven);
#include<functional>intmain(){// A regular function pointer. Only works with an empty capture clause (empty []).double(*addNumbers1)(double,double){[](doublea,doubleb){return(a+b);}};addNumbers1(1,2);// Using std::function. The lambda could have a non-empty capture clause (discussed next lesson).std::functionaddNumbers2{// note: pre-C++17, use std::function<double(double, double)> instead[](doublea,doubleb){return(a+b);}};addNumbers2(3,4);// Using auto. Stores the lambda with its real type.autoaddNumbers3{[](doublea,doubleb){return(a+b);}};addNumbers3(5,6);return0;}
使用lambda的实际类型的唯一方法是通过 auto 。与 std::function 相比,auto 还有一个好处,那就是没有开销。
不幸的是,在C++ 20之前,我们不能总是使用auto 。在实际lambda未知的情况下(例如,因为我们将lambda作为参数传递给函数,由调用者决定将传入什么lambda),我们不能在这种情况下使用 auto 。在这种情况下,可以使用std::function 来代替。
1 2 3 4 5 6 7 8 91011121314151617181920
#include<functional>#include<iostream>// We don't know what fn will be. std::function works with regular functions and lambdas.voidrepeat(intrepetitions,conststd::function<void(int)>&fn){for(inti{0};i<repetitions;++i){fn(i);}}intmain(){repeat(3,[](inti){std::cout<<i<<'\n';});return0;}
#include<algorithm>#include<array>#include<iostream>#include<string_view>intmain(){constexprstd::arraymonths{// pre-C++17 use std::array<const char*, 12>"January","February","March","April","May","June","July","August","September","October","November","December"};// Search for two consecutive months that start with the same letter.constautosameLetter{std::adjacent_find(months.begin(),months.end(),[](constauto&a,constauto&b){return(a[0]==b[0]);})};// Make sure that two months were found.if(sameLetter!=months.end()){// std::next returns the next iterator after sameLetterstd::cout<<*sameLetter<<" and "<<*std::next(sameLetter)<<" start with the same letter\n";}return0;}
输出:
1
June and July start with the same letter
在上面的例子中,我们使用了 auto 形参来获取字符串的 const引用。由于所有的字符串类型都可以通过 operator[] 访问单个字符。所以我们无需关心传入字符串是std::string、C风格字符串还是其他字符串。这样一来,我们就可以编写介绍这些类型的lambda表达式,如果将来需要修改 months 的类型,也不必修改lambda。
#include<algorithm>#include<array>#include<iostream>#include<string_view>intmain(){constexprstd::arraymonths{// pre-C++17 use std::array<const char*, 12>"January","February","March","April","May","June","July","August","September","October","November","December"};// Count how many months consist of 5 lettersconstautofiveLetterMonths{std::count_if(months.begin(),months.end(),[](std::string_viewstr){return(str.length()==5);})};std::cout<<"There are "<<fiveLetterMonths<<" months with 5 letters\n";return0;}
输出:
1
There are 2 months with 5 letters
在这个例子中,如果使用 auto,则会推断出 const char*。由于 C 风格字符串并不易用(除了operator[])。所以在这个例子中我们显式地定义参数为 std::string_view类型,这使得我们在处理数据时可以更容易。(例如,我们可以查询string view 的长度,即便传入的是一个C风格字符串数组)。
泛型lambda和静态变量
需要注意的一件事是,编译器将为 auto 解析出的每个不同类型生成唯一的lambda表达式。下面的例子展示了一个泛型lambda如何变成两个不同的lambda的:
1 2 3 4 5 6 7 8 910111213141516171819202122232425
#include<algorithm>#include<array>#include<iostream>#include<string_view>intmain(){// Print a value and count how many times @print has been called.autoprint{[](autovalue){staticintcallCount{0};std::cout<<callCount++<<": "<<value<<'\n';}};print("hello");// 0: helloprint("world");// 1: worldprint(1);// 0: 1print(2);// 1: 2print("ding dong");// 2: ding dongreturn0;}
#include<iostream>intmain(){autodivide{[](intx,inty,boolintDivision){// note: no specified return typeif(intDivision)returnx/y;// return type is intelsereturnstatic_cast<double>(x)/y;// ERROR: return type doesn't match previous return type}};std::cout<<divide(3,2,true)<<'\n';std::cout<<divide(3,2,false)<<'\n';return0;}
#include<iostream>intmain(){// note: explicitly specifying this returns a doubleautodivide{[](intx,inty,boolintDivision)->double{if(intDivision)returnx/y;// will do an implicit conversion of result to doubleelsereturnstatic_cast<double>(x)/y;}};std::cout<<divide(3,2,true)<<'\n';std::cout<<divide(3,2,false)<<'\n';return0;}
#include<algorithm>#include<array>#include<iostream>boolgreater(inta,intb){// Order @a before @b if @a is greater than @b.return(a>b);}intmain(){std::arrayarr{13,90,99,5,40,80};// Pass greater to std::sortstd::sort(arr.begin(),arr.end(),greater);for(inti:arr){std::cout<<i<<' ';}std::cout<<'\n';return0;}
#include<algorithm>#include<array>#include<iostream>#include<functional> // for std::greaterintmain(){std::arrayarr{13,90,99,5,40,80};// Pass std::greater to std::sortstd::sort(arr.begin(),arr.end(),std::greater{});// note: need curly braces to instantiate objectfor(inti:arr){std::cout<<i<<' ';}std::cout<<'\n';return0;}