1. Lambda函数的用处

lambda 表达式可以方便地构造匿名函数,如果你的代码里面存在大量的小函数,而这些函数一般只被调用一次,那么不妨将他们重构成 lambda 表达式。

C++11 的 lambda 表达式规范如下:

[ capture ] ( params ) mutable exception attribute -> ret { body } (1)
[ capture ] ( params ) -> ret { body } (2)
[ capture ] ( params ) { body } (3)
[ capture ] { body } (4)

其中

  • (1) 是完整的 lambda 表达式形式,

  • (2) const 类型的 lambda 表达式,该类型的表达式不能修改捕获("capture")列表中的值。

  • (3)省略了返回值类型的 lambda 表达式,但是该 lambda 表达式的返回类型可以按照下列规则推演出来:

    • 如果 lambda 代码块中包含了 return 语句,则该 lambda 表达式的返回类型由 return 语句的返回类型确定。

    • 如果没有 return 语句,则类似 void f(...) 函数。

  • 省略了参数列表,类似于无参函数 f()。

mutable 修饰符说明 lambda 表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获对象的 non-const 方法。

exception 说明 lambda 表达式是否抛出异常(noexcept),以及抛出何种异常,类似于void f() throw(X, Y)。

attribute 用来声明属性。

另外,capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表(详见第三节),具体解释如下:

  • [a,&b] a变量以值的方式被捕获,b以引用的方式被捕获。

  • [this] 以值的方式捕获 this 指针。

  • [&] 以引用的方式捕获所有的外部自动变量。

  • [=] 以值的方式捕获所有的外部自动变量。

  • [] 不捕获外部的任何变量。

此外,params 指定 lambda 表达式的参数。

2. Lambda函数中的变量截取

捕获:

(1)值捕获

与参数传值异同,

相同点:值捕获的前提是变量可以拷贝

不同点:被捕获的变量在lambda被创建时拷贝,而非调用时才拷贝

void f(const int*);void g(){    const int N = 10;    [=]{         int arr[N]; // not an odr-use: refers to g's const int N        f(&N); // odr-use: causes N to be captured (by copy)               // &N is the address of the closure object's member N, not g's N    }();}

(2)引用捕获

与引用传参类似,引用捕获保存的是引用,值会发生变化。

#include <iostream> auto make_function(int& x) {  return [&]{ std::cout << x << '\n'; };//return [=]{ std::cout << x << '\n'; }; //值捕获打印3,创建时传入的值是多少就是多少
} int main() {  int i = 3;  auto f = make_function(i); // the use of x in f binds directly to i  i = 5;  f(); // OK; prints 5}

(3)隐式捕获

[] 空捕获列表

[name1, name2, ...] 捕获一系列变量

[&] 引用捕获, 让编译器自行推导捕获列表

[=] 值捕获, 让编译器执行推导应用列表

void f3() {    float x, &r = x;    [=]    { // x and r are not captured (appearance in a decltype operand is not an odr-use)        decltype(x) y1; // y1 has type float        decltype((x)) y2 = y1; // y2 has type float const& because this lambda                               // is not mutable and x is an lvalue        decltype(r) r1 = y1;   // r1 has type float& (transformation not considered)        decltype((r)) r2 = y2; // r2 has type float const&    };}

当然C++14,C++17,C++20对lambda做了更多的更新,这里就不多讲了。

4. Lambda函数和STL 

lambda函数的引入为STL的使用提供了极大的方便。比如下面这个例子,当你想便利一个vector的时候,原来你得这么写:

vector<int> v;  v.push_back( 1 );  v.push_back( 2 );  //...  for ( auto itr = v.begin(), end = v.end(); itr != end; itr++ )  {      cout << *itr;  } 

现在有了lambda函数你就可以这么写:

    vector<int> v;      v.push_back( 1 );      v.push_back( 2 );      //...      for_each( v.begin(), v.end(), [] (int val)      {          cout << val;      } );  

而且这么写了之后执行效率反而提高了。因为编译器有可能使用”循环展开“来加速执行过程。

仿函数(functor)

什么是仿函数(functor)

functor的英文解释为something that performs a function,即其行为类似函数的东西。C++中的仿函数是通过在类中重载()运算符实现,使你可以像使用函数一样来创建类的对象。

仿函数(functor)的实现及使用

// this is a functor
struct add_x {add_x(int x) : x(x) {}int operator()(int y) { return x + y; }
private:int x;
};
// usage:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument
std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out;
// Pass a functor to std::transform, which calls the functor on every element
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1));
assert(out[i] == in[i] + 1); // for all i

为什么使用仿函数(functor)

  • 迭代和计算逻辑分离

使用仿函数可以使迭代和计算分离开来。因而你的functor可以应用于不同场合,在STL的算法中就大量使用了functor,下面是STL中for_each中使用functor的示例:

struct sum
{sum(int * t):total(t){};int * total;void operator()(int element){*total+=element;}
};
int main()
{int total = 0;sum s(&total);int arr[] = {0, 1, 2, 3, 4, 5};std::for_each(arr, arr+6, s);cout << total << endl; // prints total = 15;
}
  • 参数可设置

可以很容易通过给仿函数(functor)设置参数,来实现原本函数指针才能实现的功能,看下面代码:

class CalculateAverageOfPowers
{
public:CalculateAverageOfPowers(float p) : acc(0), n(0), p(p) {}void operator() (float x) { acc += pow(x, p); n++; }float getAverage() const { return acc / n; }
private:float acc;int   n;float p;
};

这个仿函数的功能是求给定值平方或立方运算的平均值。只需要这样来声明一个对象即可:

CalculateAverageOfPowers my_cal(2);
  • 有状态

与普通函数另一个区别是仿函数(functor)是有状态的,所以可以进行诸如下面这种操作:

CalculateAverage avg;
avg = std::for_each(dataA.begin(), dataA.end(), avg);
avg = std::for_each(dataB.begin(), dataB.end(), avg);
avg = std::for_each(dataC.begin(), dataC.end(), avg);

对多个不同的数据集进行取平均。

  • 性能

我们看一下2中写的代码:

std::transform(in.begin(), in.end(), out.begin(), add_x(1));

编译器可以准确知道std::transform需要调用哪个函数(add_x::operator)。这意味着它可以内联这个函数调用。而如果使用函数指针,编译器不能直接确定指针指向的函数,而这必须在程序运行时才能得到并调用。

一个例子就是比较std::sort 和qsort ,STL的版本一般要快5-10倍

  • 总结

当然,前3点都可以使用传统的函数和指针实现,但是用仿函数(functor)可以让这种实现变的更加简单。

函数对象包装器之std::function

std::function   是C++11中又一个新的概念。想想在之前的C++中,function一直是一个尴尬的存在,它作为程序的一部分,被“钉死”在程序的代码段,而且可调用的对象五花八门。

C++11打破了这个尴尬,统一可调用对象的概念,可调用对象有以下几种定义:

  • 是一个函数指针,参考 C++ 函数指针和函数类型;

  • 是一个具有operator()成员函数的类的对象;

  • 可被转换成函数指针的类对象;

  • 一个类成员函数指针;

C++中可调用对象的虽然都有一个比较统一的操作形式,但是定义方法五花八门,这样就导致使用统一的方式保存可调用对象或者传递可调用对象时,会十分繁琐。C++11中提供了std::function和std::bind统一了可调用对象的各种操作

举个例子:

// 普通函数int add(int a, int b){return a+b;} 
// lambda表达式auto mod = [](int a, int b){ return a % b;}
// 函数对象类struct divide{    int operator()(int denominator, int divisor){        return denominator/divisor;    }};

上述三种可调用对象虽然类型不同,但是共享了一种调用形式:

int(int ,int)

std::function就可以将上述类型保存起来,如下:

std::function<int(int ,int)>  a = add; std::function<int(int ,int)>  b = mod ; std::function<int(int ,int)>  c = divide();

总结一下:

std::function是一个类模版,是一种通用、多态的函数封装。

std::function对象可以对普通函数、Lambda表达式、函数指针、以及其它函数对象等 进行存储、复制、和调用操作。

std::function对象是对可调用实体类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的)。

std::function通常是一个函数对象类,它包装其它任意的函数对象,被包装的函数对象具有类型为T1, …,TN的N个参数,并且返回一个可转换到R类型的值。

std::function使用 模板转换构造函数接收被包装的函数对象;特别是,闭包类型可以隐式地转换为std::function。

最简单的理解就是:

通过std::function对可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的std::function对象;让我们不再纠结那么多的可调用实体

下边贴段代码加深一下理解:

#include <functional>#include <iostream>using namespace std;
std::function< int(int) > Functional;
// 普通函数int TestFunc(int a){    return a;}
// Lambda表达式auto lambda = [](int a)->int{ return a; };
// 仿函数(functor)class Functor{public:    int operator()(int a){        return a;    }};
// 1.类成员函数// 2.类静态函数class TestClass{public:    int ClassMember(int a) { return a; }    static int StaticMember(int a) { return a; }};
int main(){    // 普通函数    Functional = TestFunc;    int result = Functional(10);    cout << "普通函数:"<< result << endl;
    // Lambda表达式    Functional = lambda;    result = Functional(20);    cout << "Lambda表达式:"<< result << endl;
    // 仿函数    Functor testFunctor;    Functional = testFunctor;    result = Functional(30);    cout << "仿函数:"<< result << endl;
    // 类成员函数    TestClass testObj;    Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);    result = Functional(40);    cout << "类成员函数:"<< result << endl;
    // 类静态函数    Functional = TestClass::StaticMember;    result = Functional(50);    cout << "类静态函数:"<< result << endl;
    return 0;}

lambda 和 std::function相关推荐

  1. C++ lambda表达式 std::function 深层详解

    原文: <Under the hood of lambdas and std::function> 本文是根据原文的翻译. 文章目录 什么是lambda? lambda的使用语法 按值捕获 ...

  2. C++ 11三个新特性的简单使用 - std::function、lambda 表达式、智能指针

    使用Dev C++:先设置Dev C++ 支持 C++ 11:见此文后部: https://blog.csdn.net/bcbobo21cn/article/details/111466179 1 s ...

  3. 函数指针amp;绑定: boost::functoin/std::function/bind

    see link: https://isocpp.org/wiki/faq/pointers-to-members function vs template: http://stackoverflow ...

  4. C++11中std::function的使用

    类模版std::function是一种通用.多态的函数封装.std::function的实例可以对任何可以调用的目标实体进行存储.复制.和调用操作,这些目标实体包括普通函数.Lambda表达式.函数指 ...

  5. C++11 std::bind std::function 高级用法

    C++11 std::bind std::function 高级用法 (c++11的新特性) 原文:https://blog.csdn.net/yangjie6898862/article/detai ...

  6. C++中std::function和std::bind

    1.可调用对象 可调用对象有一下几种定义: 是一个函数指针,参考 C++ 函数指针和函数类型: 是一个具有operator()成员函数的类的对象: 可被转换成函数指针的类对象: 一个类成员函数指针: ...

  7. callable object与新增的function相关 C++11中万能的可调用类型声明std::function...

    在c++11中,一个callable object(可调用对象)可以是函数指针.lambda表达式.重载()的某类对象.bind包裹的某对象等等,有时需要统一管理一些这几类对象,新增的function ...

  8. C++11 std::function

    @time 2019-07-07 @author Ruo_Xiao 1.头文件 #include <functional> 2.作用 类模版 std::function 是一种通用.多态的 ...

  9. C++11: std::function<void()> func

    std::function介绍 类模版std::function是一种通用.多态的函数封装.std::function的实例可以对任何可以调用的目标实体进行存储.复制.和调用操作,这些目标实体包括普通 ...

最新文章

  1. 关于nil和 null和NSNull的相关问题
  2. Eclipse MAT插件下载地址
  3. python爬虫人门(10)Scrapy框架之Downloader Middlewares
  4. LibSVM学习(二)——第一次体验libSvm
  5. neo4j 两个点创建关系_Neo4j:找到两个纬度/经度之间的中间点
  6. 前端学习(2684):重读vue电商网站5之登录页面总结如何进行表单验证
  7. 老码农90%的程序员都是瞎努力!这份路线教你成为高手
  8. Flask插件---flask_script与flask_migrate
  9. java 取数组的前90位_java 从int数组中获取最大数的方法
  10. 完美使用application cache几点心得
  11. c语言标准库内存分配监控,C语言的本质(25)——C标准库之内存管理
  12. 把苦难装在心里--《赢在中国》(2008-05-27)
  13. aoi服务器图标删除后怎么找回,Windows10系统桌面图标被误删了如何找回【图文教程】...
  14. $.ajax.submit,jQuery中的AjaxSubmit使用讲解
  15. 电子信息工程专业概论_本科专业介绍 | 电子信息工程
  16. LSB图像隐写和峰值信噪比计算的python实现
  17. 两台计算机互联方案,如何连接两台电脑
  18. androidのBack返回键,home键,menu键监听使用
  19. 王家林人工智能AI第九节课:AI的上帝视角:神经网络能够完成各种计算模式的根本原因及神经网络能够识别图片宇宙密码 老师微信13928463918
  20. 王者荣耀最低战力查询-王者战区修改

热门文章

  1. 从入门到放弃,.net构建博客系统(二):依赖注入
  2. JSON Web Tokens(JWT)
  3. gitlab 目录tag_【环境搭建】gitlab-ci(持续集成)
  4. [网络安全自学篇] 五十八.Windows安全缺陷利用之再看CVE-2019-0708及反弹shell防御措施
  5. [Python人工智能] 七.加速神经网络、激励函数和过拟合
  6. 【数据结构与算法】之面试必考的“二分算法”系统梳理
  7. 2019第十届蓝桥杯C/C++ B组省赛 —— 第三题:数列求值
  8. 征战蓝桥 —— 2014年第五届 —— C/C++A组第10题——波动数列
  9. SLAM 学习与开发经验分享
  10. 【Linux】一步一步学Linux——systemctl命令(147)