当前位置: 首页 > news >正文

(C++) 从stl算法的谓词 分析lambda表达式的本质

文章目录

  • 前言
    • 辅助工具
    • 基本代码
  • 原始方法
    • 函数指针法
    • 仿函数法
    • 分析总结
  • lambda表达式
    • 直接写在谓词处
    • auto接收lambda表达式
    • 函数指针接收lambda表达式
    • std::function接收lambda表达式
    • 分析总结
  • C++11之后的lambda表达式
    • C++14增强的例子
    • C++17,20的增强
  • END

前言

lambda表达式是C++11增加的一个新特性,深受各位开发者的喜爱。

而探究lambda表达式的本质是什么,就是本文的主要内容。

辅助工具

源码探查工具:C++ Insights (cppinsights.io)

这个网站可以根据源码生成中间代码,辅助学习者学习C++的中间过程。这个网站是基于clang实现的。

本文的编译版本均为C++11

基本代码

这里就用std::sort()的第三个参数,这里需要的谓词来进行分析。

#include <algorithm>
#define M 10

int main() {
    int arr[M];

    std::sort(arr, arr + M);

    return 0;
}

原始方法

先来看看在C++11之前是怎么实现的

函数指针法

Source:

#include <algorithm>
#define M 10

bool cmp(const int &x, const int &y) {
    return x < y;
}

int main() {
    int arr[M];

    std::sort(arr, arr + M, cmp);

    return 0;
}

Insight:

#include <algorithm>
#define M 10

bool cmp(const int & x, const int & y)
{
  return x < y;
}


int main()
{
  int arr[10];
  std::sort(arr, arr + 10, cmp);
  return 0;
}

仿函数法

Source:

#include <algorithm>
#define M 10

class Cmp {
public:
    bool operator()(const int &x, const int &y) {
        return x < y;
    }
};

int main() {
    int arr[M];

    std::sort(arr, arr + M, Cmp());

    return 0;
}

Insight:

#include <algorithm>
#define M 10

class Cmp
{
  
  public: 
  inline bool operator()(const int & x, const int & y)
  {
    return x < y;
  }
  
  // inline constexpr Cmp(const Cmp &) noexcept = default;
  // inline constexpr Cmp(Cmp &&) noexcept = default;
};



int main()
{
  int arr[10];
  std::sort(arr, arr + 10, Cmp(Cmp()));
  return 0;
}

分析总结

可见在C++11之前,无论是函数指针,还是仿函数,都是比较原始的实现谓词的方式

lambda表达式

直接写在谓词处

开门见山,lambda表达式就展开成了一个匿名的内并且还是以仿函数的形式出现的

展开的代码直接放到本地编译器,也是可以正常编译运行的

由上面的原始方法可知,去掉inline operator retType_7_29 () const noexcept{}static inline bool __invoke(const int & x, const int & y){}可是没问题的

Source:

#include <algorithm>
#define M 10

int main() {
    int arr[M];

    std::sort(arr, arr + M, [](const int &x, const int &y) { return x < y; });

    return 0;
}

Insight:

#include <algorithm>
#define M 10

int main()
{
  int arr[10];
    
  class __lambda_7_29
  {
    public: 
    inline bool operator()(const int & x, const int & y) const
    {
      return x < y;
    }
    
    using retType_7_29 = bool (*)(const int &, const int &);
    inline operator retType_7_29 () const noexcept
    {
      return __invoke;
    };
    
    private: 
    static inline bool __invoke(const int & x, const int & y)
    {
      return __lambda_7_29{}.operator()(x, y);
    }
    
    public: 
    // inline /*constexpr */ __lambda_7_29(const __lambda_7_29 &) noexcept = default;
    // inline /*constexpr */ __lambda_7_29(__lambda_7_29 &&) noexcept = default;
    // inline __lambda_7_29 & operator=(const __lambda_7_29 &) /* noexcept */ = delete;
    
  };
  
  std::sort(arr, arr + 10, __lambda_7_29(__lambda_7_29{}));
  return 0;
}

下面,我们看看不直接写在谓词处,而是用变量接收lambda表达式是怎么样的

auto接收lambda表达式

可见auto识别成了这个昵称对象的类型。

Source:

#include <algorithm>
#define M 10

int main() {
    int arr[M];

    std::sort(arr, arr + M, [](const int &x, const int &y) { return x < y; });

    return 0;
}

Insight:

#include <algorithm>
#define M 10

int main()
{
  int arr[10];
    
  class __lambda_7_16
  {
    public: 
    inline bool operator()(const int & x, const int & y) const
    {
      return x < y;
    }
    
    using retType_7_16 = bool (*)(const int &, const int &);
    inline operator retType_7_16 () const noexcept
    {
      return __invoke;
    };
    
    private: 
    static inline bool __invoke(const int & x, const int & y)
    {
      return __lambda_7_16{}.operator()(x, y);
    }
    
    public: 
    // inline /*constexpr */ __lambda_7_16(const __lambda_7_16 &) noexcept = default;
    // inline /*constexpr */ __lambda_7_16(__lambda_7_16 &&) noexcept = default;
    // inline __lambda_7_16 & operator=(const __lambda_7_16 &) /* noexcept */ = delete;
    
  };
  
  __lambda_7_16 cmp = __lambda_7_16(__lambda_7_16{});
  std::sort(arr, arr + 10, __lambda_7_16(cmp));
  return 0;
}

而在本地的vscode中,autocmp的自动识别却不一样

auto cmp = [](const int &x, const int &y) { return x < y; };

// auto 的自动识别
class lambda [](const int &x, const int &y)->bool
// cmp 的自动识别
bool cmp(const int &x, const int &y)

函数指针接收lambda表达式

当左值的类型确定后,等号右侧若不是完全匹配的配型,一般需要强转

这里很明显的用static_cast<bool (*)(const int &, const int &)>进行了强转

而强转的内容是__lambda_6_45{}.operator __lambda_6_45::retType_6_45()查看源码可见实质是把__invoke这个函数指针传出去了,而__invoke的内容就是调用了仿函数,这是比较常见的一种类的内部保护机制

注意这里是把仿函数的,函数类型指针做了一个符号重载

using retType_6_45 = bool (*)(const int &, const int &);

inline operator retType_6_45 () const noexcept {}

Source:

#include <algorithm>
#define M 10

int main() {
    int arr[M];
    bool (*cmp)(const int &, const int &) = [](const int &x, const int &y) {
        return x < y;
    };
    std::sort(arr, arr + M, cmp);

    return 0;
}

Insight:

#include <algorithm>
#define M 10

int main()
{
  int arr[10];
      
  class __lambda_6_45
  {
    public: 
    inline bool operator()(const int & x, const int & y) const
    {
      return x < y;
    }
    
    using retType_6_45 = bool (*)(const int &, const int &);
    inline operator retType_6_45 () const noexcept
    {
      return __invoke;
    }
    
    private: 
    static inline bool __invoke(const int & x, const int & y)
    {
      return __lambda_6_45{}.operator()(x, y);
    }
    
    
  };
  
  using FuncPtr_6 = bool (*)(const int &, const int &);
  FuncPtr_6 cmp = static_cast<bool (*)(const int &, const int &)>(__lambda_6_45{}.operator __lambda_6_45::retType_6_45());
  std::sort(arr, arr + 10, cmp);
  return 0;
}

std::function接收lambda表达式

函数指针一直是一个比较头痛的内容。在C++11推出了std::function大大改善了接收函数指针的方式

所在头文件:#include <functional>

看的出来,这就是一个再封装一层的对象而已,将lambda的匿名对象,强转成std::function对应的对象

Source:

#include <algorithm>
#define M 10

int main() {
    int arr[M];
    bool (*cmp)(const int &, const int &) = [](const int &x, const int &y) {
        return x < y;
    };
    std::sort(arr, arr + M, cmp);

    return 0;
}

Insight:

#include <algorithm>
#include <functional>
#define M 10

int main()
{
  int arr[10];
    
  class __lambda_7_41
  {
    public: 
    inline bool operator()(const int & x, const int & y) const
    {
      return x < y;
    }
    
    using retType_7_41 = bool (*)(const int &, const int &);
    inline operator retType_7_41 () const noexcept
    {
      return __invoke;
    };
    
    private: 
    static inline bool __invoke(const int & x, const int & y)
    {
      return __lambda_7_41{}.operator()(x, y);
    }
    
    public: 
    // inline /*constexpr */ __lambda_7_41(const __lambda_7_41 &) noexcept = default;
    // inline /*constexpr */ __lambda_7_41(__lambda_7_41 &&) noexcept = default;
    
  };
  
  std::function<bool (int, int)> cmp = std::function<bool (int, int)>(std::function<bool (int, int)>(__lambda_7_41(__lambda_7_41{})));
  std::sort(arr, arr + 10, std::function<bool (int, int)>(cmp));
  return 0;
}

分析总结

用最简单的一句话总结就是,lambda表达式是匿名类的仿函数

而编译器就是想尽各种办法去调用到这个仿函数

C++11之后的lambda表达式

在C++11之后,C++14,17,20都不断的对lambda表达式进行了优化和增强

下面用C++14的增强举个小例子

注意,无论怎么变,lambda表达式的本质还是匿名类的仿函数

C++14增强的例子

在C++14中,可以用auto作为参数实现类似泛型的操作

而查看展开后的源码发现,其实就是对仿函数改为了模板编程

每次不用参数的调用,都会展开一份特化的代码

Source:

int main() {
    int num = 10;
    auto fun = [var = num](auto x) mutable { return x; };

    fun(1);
    fun('a');

    return 0;
}

Insight:

int main()
{
  int num = 10;
    
  class __lambda_3_16
  {
    public: 
    template<class type_parameter_0_0>
    inline auto operator()(type_parameter_0_0 x)
    {
      return x;
    }
    
    #ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline int operator()<int>(int x)
    {
      return x;
    }
    #endif
    
    
    #ifdef INSIGHTS_USE_TEMPLATE
    template<>
    inline char operator()<char>(char x)
    {
      return x;
    }
    #endif
    
    private: 
    int var;
    public: 
    // inline /*constexpr */ __lambda_3_16(__lambda_3_16 &&) noexcept = default;
    __lambda_3_16(int & _var)
    : var{_var}
    {}
    
  };
  
  __lambda_3_16 fun = __lambda_3_16(__lambda_3_16{num});
  fun.operator()(1);
  fun.operator()('a');
  return 0;
}

C++17,20的增强

C++17,20主要是对this捕获的增强,无状态的构造和复制等等。这里就不再展开例子了。




END

相关文章:

  • 金华城乡建设部网站首页/长尾关键词搜索
  • 跨境电商平台有哪些?列举5个/seo信息网
  • 网站建设员工资/网站的优化与推广分析
  • 做网站建设工资多少/在百度做广告多少钱
  • wordpress研究/深圳网络广告推广公司
  • 做网站是干嘛的/茂名网站建设制作
  • 机器学习的相关概念与建模流程
  • C语言文件操作(一)
  • 故障分析 | 库表名-大小写不规范,运维两行泪
  • 【JavaScript】对象
  • Request(请求)、Response(响应)跟Servlet的方法参数对接
  • 4.二级缓存解析
  • 应用加固 - HTTPCore DES加密防护优化
  • Redis作为缓存应用场景分析
  • kaggle竞赛 | Instant Gratification
  • Spring Boot(五十三):SpringBoot Actuator之简单实现
  • 【Python语言基础】——Python 集合
  • 关于Linux部署Tomcat的访问问题