【问题标题】:Nested lambda and mutable keyword嵌套的 lambda 和可变关键字
【发布时间】:2020-02-16 16:56:29
【问题描述】:

考虑以下代码:

void f() {
    int a = 3;
    [=]() {
        [=] () mutable {
            a = 5;
        }();
    }();
}

它可以在 Clang (https://godbolt.org/z/IEXotM) 上编译,但不能在 GCC (https://godbolt.org/z/xWXFe6) 上编译。 GCC 错误:

<source>: In lambda function:

<source>:5:15: error: assignment of read-only variable 'a'

    5 |             a = 5;

      |             ~~^~~

Compiler returned: 1

根据https://en.cppreference.com/w/cpp/language/lambda

说明符的可选序列。允许使用以下说明符:

可变:允许body修改copy捕获的参数,并 调用它们的非常量成员函数

而且 Clang 在这里的行为似乎是正确的。是这样吗?

【问题讨论】:

  • 我建议你在 GCC 的 bugzilla 中创建一份错误报告,如果该报告尚不存在的话。很有可能,六年后有人甚至会修复它。

标签: c++ c++11 gcc lambda clang


【解决方案1】:

在顶级 lambda 上指定 mutable 可修复 GCC 构建:https://godbolt.org/z/qGGBgs

似乎在 GCC 中“捕获”流通过 lambda。即在嵌套的 lambda 捕获中只能捕获在上述 lambda 中捕获的内容。因此,如果 a 在顶级 lambda 中未被捕获为可变,则嵌套的 lambda 无法使其突然变为可变。

不过,我不知道这是否符合 C++ 规范。

【讨论】:

  • 但它是按值捕获,内部 lambda 不会改变顶层 lambda 中的 a(它也是按值捕获)...
  • 啊,对,公平点。那么,GCC 可能无法正确支持这一点。它的行为对我来说也没有意义。
  • @JohnsonSteward 即使按价值捕获,您也需要可变:stackoverflow.com/questions/5501959/…
【解决方案2】:

如果您从引用的链接底部阅读,您可以阅读:

如果嵌套的 lambda m2 捕获的东西也被 立即包围 lambda m1,然后 m2 的捕获被转换为 如下:

  • 如果封闭的 lambda m1 通过复制捕获,m2 正在捕获 m1 闭包类型的非静态成员,而不是原始变量或 这个。
  • 如果封闭的 lambda m1 通过引用捕获,则 m2 正在捕获原始变量或 this。

    #include <iostream>
    
    int main()
    {
    int a = 1, b = 1, c = 1;
    
    auto m1 = [a, &b, &c]() mutable {
        auto m2 = [a, b, &c]() mutable {
            std::cout << a << b << c << '\n';
            a = 4; b = 4; c = 4;
        };
        a = 3; b = 3; c = 3;
        m2();
    };
    
    a = 2; b = 2; c = 2;
    
    m1();                             // calls m2() and prints 123
    std::cout << a << b << c << '\n'; // prints 234
    }
    

因此,您从封闭的 lambda 中捕获值并对其进行修改,您还需要使其在封闭的 lambda 中也是可变的。

【讨论】:

  • 仍然没有意义:“m2 正在捕获 m1 的闭包类型的非静态成员,而不是原始变量或 this。”:如果 m2 捕获了 m1 的闭包类型会有什么问题 by_value,然后对其进行变异(因为指定了 mutable)?该示例使用了 double mutable,因为封闭的 lambda 本身也修改了 a、b 和 c。
  • @JohnsonSteward 您正在修改 m2 中属于 m1 的值。无论您是通过 lambda 修改还是直接修改都没有关系。如果你修改了属于 m1 的值,无论如何你需要使用 mutable。
  • 在我提出的情况下,m2 正在捕获按值。或者说,m2不是修改m1的值(或者通过引用来捕获),而是仅仅保留那个值的一个副本(无论是m1的值还是整个闭包),所以没有意义。
  • @JohnsonSteward 检查此wandbox.org/permlink/OtWoGXBLXiMAvkBW
  • @JohnsonSteward 请阅读我强调的部分。 m2 不捕获原始变量。所以你正在修改属于 m1 的值。
猜你喜欢
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
  • 2018-10-12
  • 2011-01-26
相关资源
最近更新 更多