C++ lambda通过复制父lambda的捕获值进行捕获

10
尝试编译以下代码:
#include <functional>

void test() {

    int a = 5;

    std::function<void()> f = [a](){
        [a]()mutable{ // isn't it capture 'a' by copy???
            a = 13; // error: assignment of read-only variable 'a'
        }();
    };

}

出现了“error: assignment of read-only variable 'a'”错误。

通过在捕获变量a时添加花括号来更改代码:

#include <functional>

void test() {

    int a = 5;

    std::function<void()> f = [a](){
        [a{a}]()mutable{ // this explicitly copies a
            a = 13; // error: assignment of read-only variable ‘a’
        }();
    };

}

消除了编译错误。

我想知道为什么会这样?第一个变量不是等同于第二个吗?

这是在使用Debian版本的g++ 8.3.0时发生的。

clang++版本7.0.1可以成功编译它。

g++存在缺陷吗?


1
原始变量 a 是局部的吗?全局的吗? - StoryTeller - Unslander Monica
我尝试使用a局部变量,但还没有尝试过全局变量。 - igagis
1
将两个lambda函数都声明为mutable,g++就能够接受这段代码。很奇怪。 - chi
如果是全局变量a,那么这个错误(或者应该是警告?)是有效的,因为全局变量不需要被捕获,它们可以在lambda内部直接访问。 - igagis
@chi,这就像在那种情况下g++通过引用捕获了a - igagis
clang和msvc都接受此代码。 - Marek R
1个回答

5

[C++11: 5.1.2/14]: 如果一个实体被隐式捕获并且捕获默认设置为=,或者如果它被使用没有包括一个&的捕获方式显式地捕获,那么它将被以复制方式捕获。对于每个通过复制被捕获的实体,在闭包类型中声明一个未命名的非静态数据成员。这些成员的声明顺序是不确定的。如果实体不是对对象的引用,则此类数据成员的类型为相应捕获实体的类型;否则为引用类型。

在您可变的lambda内部,a的类型为const int,因为它是从封闭的constlambdaconst int a进行了复制捕获。因此,使两个lambda均为可变可以解决此问题。


我认为这里有些奇怪。我认为实际上指定了内部 [a] 是指原始的 int a = 5; 而不是外部 lambda 的闭包类型的数据成员。也就是说,在 lambda [a]() { use(a); } 中,use(a) 基本上被认为是引用在作用域中的 a好像没有 lambda 存在,然后才拦截 lambda。例如,尝试检查外部(const)lambda 中的 a 是否为 const。然后,内部捕获列表是指非 constint a,并且将捕获非 const。GCC bug? - HTNW
也许在这种情况下,bug可能是decltype(a)看到的是外部的a,而不是lambda中的a,并且在这种情况下,g++clang++的行为完全相同,所以这要么是两个编译器中的bug,要么是我们目前尚未理解的一些有效行为。 - igagis
这就是问题所在:根据标准,我相当确定没有“lambda的a。Lambda内部的东西直接引用外部的东西,然后Lambda拦截它们。因此,内部捕获列表将引用外部的a,使其自己的数据成员非const,然后外部Lambda重定向用于初始化内部Lambda的a的读取到其数据成员。 - HTNW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接