【问题标题】:Deducing Lambda Capture Types推断 Lambda 捕获类型
【发布时间】:2019-06-28 18:21:23
【问题描述】:

我最近发现,在 lambda 中按值捕获 const 对象意味着 labmda 主体内的变量(即 lambda 的数据成员)也是 const
例如:

const int x = 0;
auto foo = [x]{
  // x is const int
};

draft for C++17 的第 8.1.5.2 节中提到了此行为:

对于通过副本捕获的每个实体,在闭包类型中声明了一个未命名的非静态数据成员。这 这些成员的声明顺序未指定。这种数据成员的类型是被引用的类型 如果实体是对对象的引用,则对被引用函数类型的左值引用如果实体 是对函数的引用,否则是相应捕获实体的类型。一个成员 匿名工会不得抄袭。

我希望推断捕获变量的类型与推断 auto 相同。
是否有充分的理由为捕获的类型设置不同的类型推断规则?

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    在您的示例中,无法修改 x,因为 lambda 不是 mutable,这使得函数调用运算符 const。但即使 lambda 是 mutable,引用的段落确实在 lambda const int 中产生了 x 的类型。

    如果我没记错的话,这是在 C++11 中经过深思熟虑的设计决定,以使在 lambda 中使用 x 的行为类似于在封闭范围中使用 x。也就是说,

    void foo(int&);
    void foo(const int&);
    const int x = 0;
    foo(x);  // calls foo(const int&)
    auto foo = [x]() mutable {
        foo(x);  // also calls foo(const int&)
    };
    

    这有助于避免出现错误,例如,某些代码从显式循环重写为使用 lambda 调用标准库算法。

    如果我对这段回忆有误,希望有正确答案的人能介入并写下他们自己的答案。

    【讨论】:

      【解决方案2】:

      不是推理的答案;已经有综合答案here

      对于那些想知道如何捕获 const 变量的非 const 副本的人,您可以使用带有初始化程序的捕获:

      const int x = 0;
      auto foo = [x = x]() mutable {
          // x is non-const
      };
      

      不过,这需要 C++14。与 C++11 兼容的解决方案是在 lambda 之外制作副本:

      const int x = 0;
      int copy = x;
      auto foo = [copy]() mutable {
          // copy is non-const
      };
      

      【讨论】:

      • 吹毛求疵,但这需要C++14 标签
      • @LWimsey 哦;考虑到 C++11 标签的好点;我没注意到。
      • 你的意思是auto foo = [copy]...
      • @PaulSanders 是的,确实如此。
      【解决方案3】:

      原因是lambda中的operator()默认是const

      int main()
      {
          const int x = 0;
          auto foo = [x](){}; // main::$_0::operator()() const
          foo();
      }
      

      所以你必须使用mutable lambda:

      int main()
      {
          const int x = 0;
          auto foo = [x=x](){}; // main::$_0::operator()()
          foo();
      }
      

      【讨论】:

      • mutable 将使operator() 成为非const,但是对于捕获的对象(没有初始化器),类型是被压制的,在这种情况下包括const
      【解决方案4】:

      是否有充分的理由为捕获的类型设置不同的类型推断规则?

      这是经过深思熟虑的决定(其理由/理由引自以下 CWG 756),并且是对原始 lambda 提案的重写的一部分,

      通过

      在 2009 年 3 月的 Summit 会议期间,大量与 C++0x 相关的问题 Lambdas 由核心工作组 (CWG) 提出和审查。决定清楚后 对于大多数这些问题的方向,CWG 得出结论,最好重写该部分 在 Lambdas 上实现该方向。本文介绍了这种重写。

      特别是其 CWG 756 的决议 [emphasis mine]:

      [...] 考虑以下示例:

      void f() {
        int const N = 10;
        [=]() mutable { N = 30; }  // Okay: this->N has type int, not int const.
        N = 20;  // Error.
      }
      

      也就是说,作为闭包对象成员的N不是const, 即使捕获的变量是 const。这看起来很奇怪,因为 捕捉基本上是一种捕捉本地环境的方法 避免终身问题的方式。更严重的是,类型的变化 表示 decltype、重载决议和模板的结果 参数推导应用于 lambda 中的捕获变量 表达式可以不同于包含 lambda 表达式,这可能是一个微妙的错误来源。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-23
        相关资源
        最近更新 更多