在嵌套的 Lambda 中捕获 Lambda 的静态

Capturing a Lambda's static in a Nested Lambda

this answer中我使用这个代码:

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 yields:

0 0 0 0 0 0 0 0 0 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 yields:

0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

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

An internal error has occurred in the compiler.

如果我将捕获嵌套捕获更改为显式捕获 row,我会收到编译器错误:

identifier in capture must be a variable with automatic storage duration declared in the reaching scope of the lambda

我可以在嵌套的 lambda 中捕获 static 吗?貌似还行,就是问题太多了!

编辑:

如果我更改嵌套的 lambda 的参数类型,我 可以 得到 Visual Studio 2015 进行编译并提供与 Clang 3.7.0 相同的输出从 const auto&const int&。这看起来完全无关,但它有效。

如果我尝试明确捕获 row,这将不起作用。在那种情况下,我仍然会收到编译器错误:

identifier in capture must be a variable with automatic storage duration declared in the reaching scope of the lambda

我在这里报告了一个 Visual Studio 2015 错误:https://connect.microsoft.com/VisualStudio/feedback/details/1930409/capturing-a-lambdas-static-in-a-nested-lambda

内部编译器错误(ICE)始终是错误。

我们不需要捕获静态存储持续时间的变量,但我们确实需要捕获 odr-used 的自动变量。来自 C++ 标准草案 5.1.2:

The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator, but for purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming idexpressions referring to non-static class members into class member access expressions using (*this) (9.3.1), the compound-statement is considered in the context of the lambda-expression.

所以 row 应该在内部 lambda 中可见并且:

[...]If a lambda-expression or an instantiation of the function call operator template of a generic lambda odr-uses (3.2) this or a variable with automatic storage duration from its reaching scope, that entity shall be captured by the lambda-expression.[...]

只有this和自动存储持续时间的变量如果是odr-used才需要捕获,我们可以看到只有自动变量或this[=51才定义显式捕获=]:

The identifier in a simple-capture is looked up using the usual rules for unqualified name lookup (3.4.1); each such lookup shall find an entity. An entity that is designated by a simple-capture is said to be explicitly captured, and shall be this or a variable with automatic storage duration declared in the reaching scope of the local lambda expression.

为了让 Visual Studio 和 gcc 都匹配 clang 的结果,我可以将 row 移出到全局命名空间 see it live for gcc。也正如 Fozi 指出的那样,将 const auto& /*j*/ 更改为 const int& /*j*/ 使其开始工作。

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

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

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

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

我没有看到任何会使原始程序格式错误的标准部分。将 auto 更改为 int 也不应该有所不同,polymorphic lambda proposal 引入的任何更改似乎也无法解释这种差异。