lambda原理篇
demo
int main()
{
    int inc = 10;
    auto lambda = [&inc](int i) -> int{
        return i + inc;
    };
    std::cout << lambda(1) << std::endl;
}使用规则
语法:
[捕获参数](参数) mutable -> 返回 {
    函数体
};1.1 捕获参数
捕获参数:捕获的外部变量,用于函数体内访问外部变量;不指定,则不捕获任何外部变量
捕获方式:
| &变量名 | 以引用方式捕获 | 
| 变量名 | 已拷贝方式捕获 | 
| & | 以引用方式捕获当前环境下的所有外部变量 | 
| = | 以拷贝方式捕获当前环境下的所有外部变量 | 
注意点:
1、类中this的捕获方式,[this]、[=]、[&]均是以引用方式捕获this;
2、如果需要以拷贝方式捕获this,[*this]
3、以引用方式捕获数据时,需确保捕获的数据在lambda使用时未被释放,特别是表达式跨函数栈传输时
1.2 mutable
mutable用于确认捕获的参数时候可以在lambda内部修改
有捕获原理分析
拿demo来进行分析,还原demo编译后的内容
int main()
{
    int inc = 10;
    class __lambda_7_19
    {
    public:
        inline /*constexpr */ int operator()(int i) const
        {
            return i + inc;
        }
    private:
        int &inc;
    public:
        __lambda_7_19(int & _inc)
            : inc{ _inc }
        {}
    };
    __lambda_7_19 lambda = __lambda_7_19{ inc };
    std::cout.operator<<(lambda.operator()(1)).operator<<(std::endl);
    return 0;
}对上述代码进行格式化后:
int main()
{
    class __lambda_7_19
    {
    public:
        // 重载运算符(),const代表无法修改成员变量inc
        inline int operator()(int i) const
        {
            return i + inc;
        }
        // 构造函数
        __lambda_7_19(int & _inc)
            : inc{ _inc }
        {}
    private:
        // 以引用的方式和外部变量inc共用同一块内存
        int & inc;
    };
    int inc = 10;
    // 类对象初始化
    __lambda_7_19 lambda = __lambda_7_19{ inc };
    
    std::cout.operator<<(lambda.operator()(1)).operator<<(std::endl);
    return 0;
}
从上述内容可以看出一下几点:
1、lambda表达式会转换为类对象
2、捕获的变量会变成类对象的成员变量
3、产生的类中有operator()的重载运算符,lambda表达式的参数为重载运算符()的参数,返回值为重载运算符()的返回值
无捕获原理分析
无捕获的会相对复杂一点,无捕获的会涉及到operator的第二种用法
1、operator重载运算符
2、operator用于隐式转换,比如operator int(),代表可隐式转换为int类型
class Data {
public:
    Data(int data):innerData{data}{};
    operator int (){
        return innerData;
    };
private:
    int innerData;
}无捕获举例:
#include <iostream>
int main()
{
    auto doubleLambda = [](int i) -> int {
        return i + i;
    };
    doubleLambda(10);
}还原为编译后的内容:
#include <iostream>
int main()
{
    
  class __lambda_5_25
  {
    public: 
    inline /*constexpr */ int operator()(int i) const
    {
      return i + i;
    }
    
    using retType_5_25 = auto (*)(int) -> int;
    inline constexpr operator retType_5_25 () const noexcept
    {
      return __invoke;
    };
    
    private: 
    static inline /*constexpr */ int __invoke(int i)
    {
      return __lambda_5_25{}.operator()(i);
    }
    
    
  };
  
  __lambda_5_25 doubleLambda = __lambda_5_25{};
  doubleLambda.operator()(10);
  return 0;
}格式化后:
#include <iostream>
int main()
{
    
  class __lambda_5_25
  {
    public: 
    // 重载()运算符
    inline  int operator()(int i) const
    {
      return i + i;
    }
    
    // 声明函数指针
    using retType_5_25 = auto (*)(int) -> int;
    // 这里就比较有意思了,operator的第二种用法,隐式转换,operator retType_5_25 (),隐式转换为函数指针
    inline operator retType_5_25 () const 
    {
      return __invoke;
    };
    
    private: 
    // 用私有静态成员函数,作为函数指针
    static inline  int __invoke(int i)
    {
      // 调用对象的operator ()重载的运算符
      return __lambda_5_25{}.operator()(i);
    }
    
    
  };
  
  __lambda_5_25 doubleLambda = __lambda_5_25{};
  doubleLambda.operator()(10);
  return 0;
}总结:
1、无捕获的表达式会生成隐式转换为”函数指针“的函数,用私有静态函数做承接,并通过生成对象、调用operator()重载运算符内容来完成lambda表达式函数体的调用
问题:是否可以为有捕获的lambda表达式生成隐式转换函数和静态函数?为什么?
答案:不行,因为静态函数在生成类对象时,无法指导捕获的内容,也就无法使用捕获的内容来构造类对象
lambda和函数指针的区别
从前面无捕获的lambda表达式可以看出,无捕获的lambda表达式有转换为函数指针的隐式转换函数,因此
无捕获的lambda表达式可转换为函数指针
有捕获的lambda表达式无法转换为函数指针,因为有捕获的必须传递this指针,导致无法转换
举例说明:
typedef int(*DoubleFunType)(int);
void doAction(DoubleFunType fun)
{
    fun(10);
}
int main()
{
    auto doubleLambda = [](int i) -> int {
        return i + i;
    };
    doAction(doubleLambda);
}还原编译后的内容:
typedef int(*DoubleFunType)(int);
void doAction(DoubleFunType fun)
{
  fun(10);
}
int main()
{
    
  class __lambda_12_25
  {
    public: 
    inline /*constexpr */ int operator()(int i) const
    {
      return i + i;
    }
    
    using retType_12_25 = auto (*)(int) -> int;
    inline constexpr operator retType_12_25 () const noexcept
    {
      return __invoke;
    }
    
    private: 
    static inline /*constexpr */ int __invoke(int i)
    {
      return __lambda_12_25{}.operator()(i);
    }
    
    
  };
  
  __lambda_12_25 doubleLambda = __lambda_12_25{};
  doAction(doubleLambda.operator __lambda_12_25::retType_12_25());
  return 0;
}格式化后:
// 声明函数指针
typedef int(*DoubleFunType)(int);
void doAction(DoubleFunType fun)
{
  // 调用函数指针
  fun(10);
}
int main()
{
    // 此处和上面的完全一致
  class __lambda_12_25
  {
    public: 
    inline /*constexpr */ int operator()(int i) const
    {
      return i + i;
    }
    
    using retType_12_25 = auto (*)(int) -> int;
    inline constexpr operator retType_12_25 () const noexcept
    {
      return __invoke;
    }
    
    private: 
    static inline /*constexpr */ int __invoke(int i)
    {
      return __lambda_12_25{}.operator()(i);
    }
    
    
  };
  
  __lambda_12_25 doubleLambda = __lambda_12_25{};
  // doubleLambda.operator retType_12_25() 强制调用隐式转换为函数指针,而__lambda_12_25::是域作用符,用于标识retType_12_25类型是类内部类型
  doAction(doubleLambda.operator __lambda_12_25::retType_12_25());
  return 0;
}总结:
无捕获的lambda可传递给函数指针。
lambda结合std::function构建轻量级代理
结合业务来说,直接用lambda表达式用于传递函数指针,用处不大,因为无法用有捕获的表达式传给函数指针,无捕获的表达式用处有限
而c++提供一个对象std::function,构造函数可以接收lambda表达式和函数指针,没有转换函数指针的限制,为什么呢?
拿刚刚的例子做下简单修改,把函数指针更改为std::function
typedef std::function<int(int)> DoubleFunType;
void doAction(DoubleFunType fun)
{
    fun(10);
}
int main()
{
    int time = 10;
    auto timeLambda = [time](int i) -> int {
        return time * i;
    };
    doAction(timeLambda);
}还原后:
#include <iostream>
#include <functional>
typedef std::function<int(int)> DoubleFunType;
void doAction(std::function<int (int)> fun)
{
  // 关键点在这里,std::function对象的执行是只要支持operator()运算符即可,不不是限制于函数指针
  fun.operator()(10);
}
int main()
{
  int time = 10;
    
  class __lambda_14_23
  {
    public: 
    inline /*constexpr */ int operator()(int i) const
    {
      return time * i;
    }
    
    private: 
    int time;
    public: 
    // inline /*constexpr */ __lambda_14_23(const __lambda_14_23 &) noexcept = default;
    __lambda_14_23(int & _time)
    : time{_time}
    {}
    
  };
  
  __lambda_14_23 timeLambda = __lambda_14_23{time};
  doAction(std::function<int (int)>(timeLambda));
  return 0;
}从注释中可以看出关键点:
std::function对象的执行是只要支持operator()运算符即可,不不是限制于函数指针
因此std::function可以传入带捕获的lambda表达式,从而实现轻量级代理
轻量级代理的设计实现:
https://bbs.huaweicloud.com/blogs/416963
- 点赞
- 收藏
- 关注作者
 
             
           
评论(0)