C辨析函数指针函数对象Lambda表达式及后者最优的原因
试想有一排序函数sort(int arr[])默认为升序排序,如果想更改为降序排列,最直接的方法就是在函数体中修改比较运算符,但这种修改函数体的方法不够灵活,特别是如果sort(int arr[])要封装到库(接口与实现分离)时,库使用者是无法去修改函数体的。理想的方法是修改接口,在接口在添加一函数指针做为形式参数,在函数体中调用函数指针,这样,函数体中的代码就可以固定下来,而选择升序还是降序排列,则由函数指针指向的函数来决定。
函数指针、函数对象、Lambda表达式都可以实现同样的功能:#include #include using namespace std; struct FuncObj { constexpr FuncObj() = default; int operator()(int a) { return run(a); } // this is a non-capturing lambda, the operator can be // in a static function static int run(int a) { return a; } }; typedef int (*funcp)(int a); //template //int func(int a) int func(int a, funcp foo) { foo(a); } int func(int a,FuncObj lt) { lt(a); } int funcpRelated(int a) { return a; } int main() { auto lambda = [](int a) { return a; }; funcp ptr = lambda; // 类型相同,不能捕捉 cout< f_int_obj = FuncObj(); cout<(1); // ---> this is working return 0; }
函数指针、函数对象、Lambda表达式对STL算法的使用:#include #include #include #include #include const long Size1 = 39L; const long Size2 = 100*Size1; const long Size3 = 100*Size2; bool f3(int x) {return x % 3 == 0;} bool f13(int x) {return x % 13 == 0;} int main() { using std::cout; std::vector numbers(Size1); std::srand(std::time(0)); std::generate(numbers.begin(), numbers.end(), std::rand); // using function pointers cout << "Sample size = " << Size1 << " "; int count3 = std::count_if(numbers.begin(), numbers.end(), f3); cout << "Count of numbers pisible by 3: " << count3 << " "; int count13 = std::count_if(numbers.begin(), numbers.end(), f13); cout << "Count of numbers pisible by 13: " << count13 << " "; // increase number of numbers numbers.resize(Size2); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Sample size = " << Size2 << " "; // using a functor class f_mod { private: int dv; public: f_mod(int d = 1) : dv(d) {} bool operator()(int x) {return x % dv == 0;} }; count3 = std::count_if(numbers.begin(), numbers.end(), f_mod(3)); cout << "Count of numbers pisible by 3: " << count3 << " "; count13 = std::count_if(numbers.begin(), numbers.end(), f_mod(13)); cout << "Count of numbers pisible by 13: " << count13 << " "; // increase number of numbers again numbers.resize(Size3); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Sample size = " << Size3 << " "; // using lambdas count3 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 3 == 0;}); cout << "Count of numbers pisible by 3: " << count3 << " "; count13 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 13 == 0;}); cout << "Count of numbers pisible by 13: " << count13 << " "; // std::cin.get(); return 0; }
输出:Sample size = 39 Count of numbers pisible by 3: 14 Count of numbers pisible by 13: 0 Sample size = 3900 Count of numbers pisible by 3: 1365 Count of numbers pisible by 13: 289 Sample size = 390000 Count of numbers pisible by 3: 129968 Count of numbers pisible by 13: 30393
函数数指针、函数对象、Lambda表达式的使用,可以从以下几个方面来区别:
1 距离:让定义位于使用的地方最近是一种最佳的选择。这是Lambda表达式的优势。函数对象是次优的选择,因为函数对象的类也可以定义在函数内部。最差的是函数指针了,距离最远。
2 简洁:是指代码的简洁度。Lambda表达式的代码最简洁,函数指针次之,函数对象虽然相对于函数指针来说,可以实现状态数据的存储,但代码的简洁度最差。
3 效率
这三种方法的相对效率取决于编译器内联那些东西。函数指针方法阻止了内联(能够内联的代码,执行效率比较高),因为编译器传统上不会内联其地址被获取的函数,因为函数地址的概念意味着非内联函数。而函数对象和lambda通常不会阻止内联。
4 功能
lambda有一些额外的功能。具体地说,lambad可访问作用域内的任何动态变量;要捕获要使用 的变量,可将其名称放在中括号内。如果只指定了变量名,如[z],将按值访问变量;如果在名称前加上& , 如[&count],将按引用访问变量。[&]让您能够按引用访问所有动态变量,而[=]让您能够按值访问所有动态变 量。还可混合使用这两种方式,例如[ted, &ed]让您能够按值访问ted以及按引用访问ed ,[&, ted]让您能够按值访问ted以及按引用访问其他所有动态变量,[=,&ed]让您能够按引用访问ed以及按值访问其他所有动态变量。
看下面的实例:// lambda1.cpp -- use captured variables #include #include #include #include #include const long Size = 390000L; int main() { using std::cout; std::vector numbers(Size); std::srand(std::time(0)); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Sample size = " << Size << " "; // using lambdas int count3 = std::count_if(numbers.begin(), numbers.end(), [](int x){return x % 3 == 0;}); cout << "Count of numbers pisible by 3: " << count3 << " "; int count13 = 0; std::for_each(numbers.begin(), numbers.end(), [&count13](int x){count13 += x % 13 == 0;}); cout << "Count of numbers pisible by 13: " << count13 << " "; // using a single lambda count3 = count13 = 0; std::for_each(numbers.begin(), numbers.end(), [&](int x){count3 += x % 3 == 0; count13 += x % 13 == 0;}); cout << "Count of numbers pisible by 3: " << count3 << " "; cout << "Count of numbers pisible by 13: " << count13 << " "; // std::cin.get(); return 0; }
输出:Sample size = 390000 Count of numbers pisible by 3: 130144 Count of numbers pisible by 13: 29939 Count of numbers pisible by 3: 130144 Count of numbers pisible by 13: 29939
用表格综合一下以上4项区别:
表格数据:
函数指针
函数对象
lambda表达式
距离
定位位置与使用位置
最差
次优
最优
简洁
代码的简洁度
次优
最差
最优
效率
可内联的代码效率要高
不能内联
可内联
可内联
功能
额外功能
可访问作用域内的任何动态变量
ref
StephenPrata:《C++ Primer Plus(6th 2011)》
-End-
直截了当,各专业人士对待撒泼问题,都用的什么做法?据环球日报日前报道,在美国一架飞机上,一女子试图破坏美国航空安全法案规定,企图自己拉开飞机舱门,在机组人员发现后试图阻止这一行为,被该女子的咬伤,然后这名发疯的女子就被空警控制,并
美金不值钱了?如今美债有市无价,中国表示不陪你玩了由于美国现如今的钞能力把自己的经济搞垮了,以无限印钞刺激美国国内的经济增长,最后适得其反,货物膨胀导致美金越来越不值钱,国际上美债早已热度下降,美联社看似英明的大放水举措只能让现如
南非发生了什么?当地发生暴乱,造成多人死亡,经济停滞自曼德拉总统上台后,南非就从一个发达国家变为了发展中国家,其经济和社会情况萎靡不振,到现如今,南非再次由于政府管理制度发生暴乱,南非前总统祖马由于蔑视法律被依法逮捕关押,当地人民由
美国不让世卫自己的实验室,世卫再次将矛头调向中国,意义何在?现如今的美国,本土疫情没有得到控制,非但没有加大防疫力度,还把侧重点转向中国。前一阵子,美国联合澳大利亚向中国索要赔偿,其中的原因就是想把疫情爆发的矛头指向中国病毒研究所。早期全球
中国移民成主力军,还想带走亿万资产?国家已出手整治大家也都知道,尤其是在以前的时候,我国的经济发展水平不行,尤其是相比西方的发达国家,所以很多富商富豪就会选择移民,找个发达的国家进行发展生活。说到移民这个话题。现在的移民有一部分追
钟剑秋做虔诚的追逐光的画家展览海报导言2021年8月21日,由北京靳尚谊艺术基金会与中国油画学会共同主办意大利TIAC协会协办的艺术与自然意大利考察创作汇报展于北京隆福文化中心正式开展。本次展览是对北京靳尚
都成坑的寿山石一位玩藏福建寿山石雕印章的朋友,由于经验不足,又常恐走宝,曾购入一件黄金黄都成坑所刻成的作品,误以为像石贩所说是上品田黄。事实上,二者外表十分相似,若不细心鉴别,容易混淆。价值方面
冰雪的长津湖电影长津湖刚上映没多久其票房就已经达到了惊人的20多亿,为什么会有如此高的票房呢,因为那是真实的历史。71年前在朝鲜战争中,有个没有一声枪响的战役,其悲壮程度震惊了整个世界,那就是
跨界营销新媒体营销节庆营销冰雪旅游营销百花齐放品橙旅游2020年年底,一则新疆昭苏县红衣女县长策马奔腾雪野的短视频被刷屏,事后,女县长在说到拍短视频初衷时提到希望这样的短视频能帮助一下昭苏的旅游关联行业从业者,争取一些冬季冰雪
冷资源变热经济,冰雪旅游节事的迭代之路品橙旅游近日,在央视新闻告别2020特别节目直播报道中,央视记者王冰冰亮相于查干湖第十九届冰雪渔猎文化旅游节的夜幕中。她在晶莹剔透的冰钢琴上雀跃跳动,随之而来的是钢琴般的美妙旋律。
冰雪旅游与保险品橙旅游年初,河北省张家口市崇礼区由密苑(张家口)旅游胜地有限公司经营的云顶滑雪场发生一起安全事故,导致一名男性滑雪者意外死亡,引起轩然大波。2020年12月初,在哈尔滨融创雪世界