在嵌套 Lambda 中捕获 Lambda 的静态

Capturing a Lambda's static in a Nested Lambda

本文关键字:Lambda 静态 嵌套      更新时间:2023-10-16

在这个答案中,我使用以下代码:

std::vector<std::vector<int>> imat(3, std::vector<int>(10));
std::for_each(imat.begin(), imat.end(), [&](auto& i) {
    static auto row = 0;
    auto column = 0;
    std::transform(i.begin(), i.end(), i.begin(), 
        [&](const auto& /*j*/) {
            return row * column++; 
    }); 
    ++row; 
});

但是我注意到在捕获static auto row时一些错误的行为取决于编译器。

Clang 3.7.0生成:



0 1 2 3 4 5 6 7 8 9
0 2 4 6 8 10 12 14 16 18

gcc 5.1.0 yield:





<

<

>

和Visual Studio 2015给了我一个编译时错误:

编译器内部错误。

如果我改变捕获嵌套捕获显式捕获row,我得到编译器错误:

捕获中的

标识符必须是在lambda

的到达范围内声明具有自动存储持续时间的变量。

允许在嵌套lambda中捕获static吗?这似乎是合法的,但有很多问题!

编辑:

Fozi指出,如果我将嵌套lambda的参数类型从const auto&更改为const int&,则I 可以获得Visual Studio 2015编译并给出与Clang 3.7.0相同的输出。这看起来完全不相关,但却有效。

如果我试图显式捕获row,这不起作用。在这种情况下,我仍然得到编译器错误:

捕获中的

标识符必须是在lambda

的到达范围内声明具有自动存储持续时间的变量。

我报告了VisualStudio 2015的一个bug: https://connect.microsoft.com/VisualStudio/feedback/details/1930409/capturing-a-lambdas-static-in-a-nested-lambda

内部编译错误(ICE)总是一个bug。

我们不需要捕获静态存储持续时间的变量,但我们确实需要捕获经常使用的自动变量。来自c++标准草案5.1.2:

lambda表达式的复合语句产生函数调用操作符的函数体(8.4),,但用于名称查找(3.4),确定this(9.3.2)的类型和值并转换表达式使用(*this)(9.3.1)在类成员访问表达式中引用非静态类成员,复合语句在lambda表达式的上下文中被考虑。

所以row应该在内部lambda和:

中可见

[…如果是lambda表达式或者泛型lambda odr的函数调用操作符模板的实例化-使用(3.2)this或a具有自动存储期限的变量,从其到达范围开始,该实体应由lambda表达式。[…]

捕获只需要用于this和自动存储持续时间的变量,如果它们是odr使用的,我们可以看到显式捕获只定义为自动变量或 :

简单捕获中的标识符使用非限定名称查找的常规规则查找(3.4.1);每个这样的查找将找到一个实体。由简单捕获指定的实体称为显式实体捕获的,并且应该是this或在到达范围内声明具有自动存储持续时间的变量

对于Visual Studio和gcc都匹配clang的结果,我可以将row移到全局命名空间中,参见gcc的现场。此外,Fozi指出,将const auto& /*j*/更改为const int& /*j*/可以使其开始工作。

看起来gcc接受显式捕获非自动变量作为扩展,即使显式捕获row,例如[&, &row](const auto & )仍然产生全零。

对于gcc,如果我将row的定义移动到main,那么我看到以下错误( live):

/tmp/cchzwtQI.s: Assembler messages:
/tmp/cchzwtQI.s:1572: Error: symbol `_ZL3row' is already defined

这对我来说似乎是一个编译错误。

我看不出标准的任何部分会使原始程序格式错误。将auto更改为int也不应该产生差异,并且由多态lambda提议引入的更改似乎也不能解释这种差异。