前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++避坑---lambda表达式变量的捕获与mutable关键字

C++避坑---lambda表达式变量的捕获与mutable关键字

作者头像
艰默
发布2023-05-17 21:59:06
4060
发布2023-05-17 21:59:06
举报
文章被收录于专栏:iDoitnowiDoitnow

首先我们看一个例子:

代码语言:javascript
复制
#include <iostream>
using namespace std;

int main() {
  int a = 0;
  static int b = 0;

  auto f = [=]() mutable {
    cout << "in lambda f : " << ++a << ", " << ++b << endl;
  };

  f();
  f();
  cout << "in main : " << a << ", " << b << endl;

  return 0;
}

输出结果为

代码语言:javascript
复制
in lambda f : 1, 1
in lambda f : 2, 2
in main : 0, 2

在第一次看见这个例子的时候,我预想到的af中的两次输出都应该为1,但真实的输出结果是在两次f的调用中,实现了累加,后来查阅资料发现:

lambda 表达式是纯右值表达式,它的类型是独有的无名非联合非聚合类型,被称为闭包类型(closure type)

代码语言:javascript
复制
闭包类型::operator()(形参)
返回类型 operator()(形参) { 函数体 }

当被调用时,执行 lambda 表达式的函数体。当访问变量时,访问的是它被捕获的副本(对于以复制捕获的实体)或原对象(对于以引用捕获的实体)。除非 lambda 表达式中使用了关键词 mutable,否则函数调用运算符或运算符模板的 cv 限定符都会是 const,并且无法从这个 operator() 的内部修改以复制捕获的对象。

也就是说,对于lambda表达式,编译器会将其翻译成为一个类,该类中的重载operator()成员函数就是lambda函数本体。如果lambda表达式未使用mutable修饰,则operator()函数是const类型的,使用mutable可以解除该限制。

我们使用C++ Insights工具将上述代码转换成编译器角度的源代码,结果如下:

代码语言:javascript
复制
#include <iostream>
using namespace std;

int main()
{
  int a = 0;
  static int b = 0;
    
  class __lambda_8_12
  {
    public: 
    inline /*constexpr */ void operator()()
    {
      std::operator<<(std::operator<<(std::cout, "in lambda f : ").operator<<(++a), ", ").operator<<(++b).operator<<(std::endl);
    }
    
    private: 
    int a;
    
    public:
    __lambda_8_12(int & _a)
    : a{_a}
    {}
    
  };
  
  __lambda_8_12 f = __lambda_8_12{a};
  f.operator()();
  f.operator()();
  std::operator<<(std::operator<<(std::cout, "in main : ").operator<<(a), ", ").operator<<(b).operator<<(std::endl);
  return 0;
}

注:C++ Insights 是从源码转换到更加详细的源码,把编译器看到的给展开。如 autotemplatedecltype 得到的类型、lambdarange-for 循环的转换、潜藏的隐式类型转换等。

从展开结果可以看出,实际上编译器就是把lambda表达式转化成为一个类,lambda表达式捕获的值为该类的数据成员。上例中lambda表达式被转化为类__lambda_8_12,其重载了operator(),由于使用了mutable修饰,解除了operator()const修饰(默认情况下是const的)。数据成员为捕获到的a,并将其实例化为类对象f,然后调用了两次operator(),因此a值的打印也是累加的,即两次结果分别为12

总 结

lambda表达式实际上就是一个独有的无名非联合非聚合类,其捕获的数据是它的类成员,该类重载了operator(),且默认情况下该成员函数是const,可以使用mutable关键字来去除const限定。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iDoitnow 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档