Lambda表达式与空捕获

Dus*_*teh 16 c++ lambda language-lawyer c++11

我在使用lambdas时遇到了一个有趣的案例(至少对我来说),并且想知道它是编译器错误还是标准功能允许的东西.

让我们切入追逐.有示例代码:

const int controlValue = 5;
std::vector<int> vect{ 0, 1, 2, 3 };
const auto result = std::any_of(vect.begin(), vect.end(), [](const int& item)
{
    return item == controlValue;
});
Run Code Online (Sandbox Code Playgroud)

请注意,controlValuelambda表达式不捕获变量.另外,在lambda表达式的cppreference中说明了这一点[] - captures nothing

使用VS2015编译上面的代码会产生一个错误,这并不奇怪:

error C3493: 'controlValue' cannot be implicitly captured because no default capture mode has been specified
Run Code Online (Sandbox Code Playgroud)

但是,当使用MinGWgcc 4.8.2相同的例子编译和工作.一些在线编译器包括gcc 5.4.0,clang 3.8.0给出了类似的结果.

controlValue失去它const然后所有测试的编译器给出所有期望的错误(没有捕获变量,这是好的).

在这种情况下,哪些编译器符合标准?这是否意味着某些优化或其他"黑客"在这里用于const变量?也许某些内容是隐含的?谁能解释一下这里发生的情况?

编辑:

有人指出这个问题是Lambda捕获constexpr对象 的重复.虽然答案可能有些相关(指向odr用例),但是有关在通过ref捕获时发生错误的问题.这里的主题是完全不同的,并且侧重于根本不捕获显式变量(尽管在lambda体中使用它).

在查看了更多与lambda相关的问题之后,如果有人感兴趣,我会指出使用lambda捕获的constexpr值作为数组维度 (与@Barry说的相同)建议VS2015错误并显示controlValue在此示例中设置变量以static修复VS2015下的编译.

Bar*_*rry 16

这是一个VS错误.代码完美无缺.

[expr.prim.lambda]中的规则是:

如果lambda表达式或函数的实例化调用泛型lambda odr的操作符模板- 使用(3.2)此变量或具有自其存储持续时间的变量,则该实体应由lambda表达式捕获.

根据[basic.def.odr],如果变量是odr-used的话:

变量x的名称显示为潜在评估的表达式ex,除非将lvalue-to-rvalue转换(4.1)应用于x,否则会产生一个不调用任何非平凡函数的常量表达式(5.20).,如果x是一个对象,则ex是表达式e的潜在结果集的一个元素,其中左值到右值的转换(4.1)应用于e,或者e是丢弃值表达式(第5条) ).

并且,来自[expr.const]:

条件表达式e是核心常量表达式,除非根据抽象机器(1.9)的规则评估e将评估以下表达式之一:[...]左值到右值的转换(4.1)除非它应用于整数或枚举类型的非易失性glvalue,它引用具有前面初始化的完整非易失性const对象,用常量表达式初始化

在:

return item == controlValue;
Run Code Online (Sandbox Code Playgroud)

controlValue是一个整数类型的glvalue,它指的是用常量表达式初始化的完整的非易失性const对象.因此,当我们controlValue在涉及左值到右值转换的上下文中使用时,它不会被使用.由于它没有使用,我们不需要捕获它.

当您更改controlValue - const,它不再是一个常量表达式,和平等检查ODR-使用它.由于它没有被捕获但是使用得很多,所以lambda是不正确的.


请注意,标准中出现了这样的示例:

void f(int, const int (&)[2] = {}) { }   // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
    const int x = 17;
    auto g = [](auto a) {
        f(x); // OK: calls #1, does not capture x
    };

    // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 使用`using controlValue_t = std :: integral_constant <int,controlValue>;`,然后在lambda中使用`controlValue_t {}`或`controlValue_t :: value`的解决方法可能值得一提.当然,对于MSVC,我不愿意在没有测试的情况下提出解决方法.;) (3认同)