【问题标题】:What does const lambda mean?const lambda 是什么意思?
【发布时间】:2017-02-23 19:55:35
【问题描述】:
#include <iostream>
int foo(int i)
{
    const auto a = [&i](){ i = 7; return i * i; };
    a();
    return i;
}
int main()
{
    std::cout << foo(42) << std::endl;
    return 0;
}

编译(g++ -std=c++11 -Wall -Wextra -Wpedantic main.cpp)并返回49。这让我感到惊讶,因为通过将a 声明为一个常量对象,我本来希望i 被引用为const int&amp;。显然不是,为什么?

【问题讨论】:

  • 代码的哪一部分通过 const ref 捕获了j?我不明白这个问题。你创建了一个函数来获取i,忽略它的值,使其为 7,平方,然后返回结果(49)......这正是发生的事情!你对j 所做的只是在返回之前将 49 短暂地存储在一个命名变量中。你能澄清一下你的意图吗?
  • 现在您根本没有名为j 的变量。你对这个修改后的代码所做的只是将i 设置为 7,返回 49 然后丢弃 49 的值。
  • 因为你被非常量引用捕获了。这就像问为什么可以通过int *const 修改东西。
  • 我不明白你的问题,就像@LightnessRacesinOrbit 所说的那样。
  • 我想知道 lambda 前面的 const 说明符有什么作用。

标签: c++ c++11 lambda constants


【解决方案1】:

Lambda 和非 Lambda 一样,只是它们的实现细节是隐藏的。因此,使用非 lambda 仿函数可能更容易解释:

#include <iostream>
int foo(int i)
{
    struct F {
      int &i;
      int operator()() const { i = 7; return i * i; }
    };
    const F a {i};
    a();
    return i;
}
int main()
{
    std::cout << foo(42) << std::endl;
    return 0;
}

F 有一个 int &amp; 引用成员 i。 const F 不能修改其实例数据,但对 i 的修改不是对其实例数据的修改。对其实例数据的修改会将i 重新绑定到另一个对象(无论如何都是不允许的)。

【讨论】:

    【解决方案2】:
    [&i](){ i = 7; return i * i; }
    

    主要相当于

    class Lambda
    {
    public:
        Lambda(int& arg_i) : i(arg_i) {}
    
        auto operator() () const { i = 7; return i * i;}
    private:
        int& i;
    };
    

    所以你有:

    const Lambda a(i);
    a();
    

    而const Lambda 不会提升其成员为const int&amp; i;,而是int&amp; const i;,相当于int&amp; i;。

    【讨论】:

      【解决方案3】:

      当您捕获i 时,它会被捕获为它的类型。

      所以在内部它有一个int&amp;。闭包的变量声明之前的 const 不会改变 lambda 的任何内容。

      你有 2 个选项来解决这个问题:

      const int i = 5;
      auto b = [&i]() { i++; }; //error on i++
      

      这样const int&amp; 将被捕获。

      如果由于某些原因您无法更改 i,您可以在 c++14 中执行此操作

      int i = 5;
      auto b = [i = static_cast<const int&>(i)]() { i++; }; //error on i++
      

      这会将int&amp; 转换为const int&amp; 并将这样存储在lambda 中。尽管如您所见,这更加冗长。

      【讨论】:

      • 如果他不想更改i,最好抄下来。
      • @krzaq 是的,但这对于您不想复制较大对象的情况很有用。在int 的情况下,这是不需要的。
      【解决方案4】:

      在您提供的代码中:

      int foo(int i)
      {
          const auto a = [&i](){ i = 7; return i * i; };
          a();
          return i;
      }
      

      在初始化常量 lambda 函数后,您没有分配。因此,const 在这种情况下意义不大。

      【讨论】:

      • 我没有,但你的回答对我来说似乎没有意义。
      • @krzaq 它在我的回答中指出:OP 首先初始化了常量 lambda。然后他没有给那个 lambda 分配一个不同的值,const 修饰符在这里并没有起到很大的作用。你对此有什么不明白的?
      • 我也没有。 @Vorac: Nothing... it means you wouldn't be able to alter a, or its members, but you're not trying to do that here anyway. Were you expecting it to magically change the internal int&amp; to a const int&amp;? – Lightness Races in Orbit 9 mins ago您的回答解决了第一点,而我的斗争是第二点。我编辑了这个问题,试图解决这个问题。
      • @Vorac 你不明白什么......请说明。
      • @krzaq 它在标题“const lambda 是什么意思?”中乱七八糟地问。 .在这种情况下..这并不像我之前所说的那样!
      【解决方案5】:

      您声明为 const 的内容不是匿名函数或 lambda 表达式及其参数的上下文,而只是该 lambda 表达式的引用:const auto a。

      因此,您无法更改 lambda expr 引用 a 的值,因为它是 const,但其通过引用传递的参数 &amp;i 可以在 lambda 表达式的上下文中更改。

      【讨论】:

      • a 不是参考。
      • 我还没有进入语言细节级别。我认为这足以抽象地说明分配给 lambda 表达式地址的变量的作用,即:对lambda 表达式或匿名函数...
      • 这不是参考。
      【解决方案6】:

      如果我理解正确,问题是为什么允许你改变 i,即使 a 是 const 并且可能包含对 i 作为成员的引用。

      答案是,出于同样的原因,您可以对任何对象执行此操作 - 分配给 i 不会修改 lambda 实例,它会修改它所引用的对象。

      例子:

      class A
      {
      public:
          A(int& y) : x(y) {} 
          void foo(int a) const { x = a; } // But it's const?!
      private:
          int& x;
      };
      
      int main()
      {
          int e = 0;
          const A a(e);
          a.foo(99);
          std::cout << e << std::endl;
      }
      

      编译并打印“99”,因为foo 不是在修改a 的成员,而是在修改e。
      (这有点令人困惑,但它有助于考虑哪些 对象 正在被修改并忽略它们的命名方式。)

      const 的这种“常量,但不是真的”性质是一个非常常见的混淆和烦恼来源。

      这正是指针的行为方式,更明显的是没有错:

      class A
      {
      public:
          A(int* y) : x(y) {} 
          void foo(int a) const { *x = a; } // Doesn't modify x, only *x (which isn't const).
      private:
          int* x;
      };
      

      【讨论】:

        猜你喜欢
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-07
        • 2011-08-27
        • 2020-04-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多