我在使用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;
});
请注意,lambda表达式未捕获controlValue
变量。
此外,在lambda表达式的cppreference中,声明[] - captures nothing
使用 VS2015 编译上述代码会出现错误并不奇怪:
error C3493: 'controlValue' cannot be implicitly captured because no default capture mode has been specified
然而,当 MinGW 与 gcc 4.8.2 一起使用时,相同的例子编译并运行。 一些在线编译器包括 gcc 5.4.0 , clang 3.8.0 给出了类似的结果。
当controlValue
丢失其const
时,所有经过测试的编译器都会给出所有期望的错误(没有捕获该变量,这很好)。
在这种情况下,哪些编译器符合标准? 这是否意味着某些优化或其他“黑客”在这里用于const变量?也许某些内容是隐含的? 谁能解释一下这里发生的情况呢?
修改
有人指出这个问题与Lambda capturing constexpr object 重复。虽然答案可能有些相关(指向odr用例),但是有关在通过ref捕获时发生错误的问题。这里的主题是完全不同的,并侧重于根本不捕获显式变量(尽管在lambda体中使用它)。
在查看了更多与lambda相关的问题后,如果有人感兴趣,我会指向Using lambda captured constexpr value as an array dimension
(与@Barry说的相同)建议VS2015错误并显示在示例中设置controlValue
变量这里static
修复了VS2015下的编译。
答案 0 :(得分:16)
这是一个VS错误。代码完美无缺。
[expr.prim.lambda]中的规则是:
如果lambda表达式或函数的实例化调用一般lambda odr的运算符模板(3.2)这个或具有自动存储持续时间的变量到达范围,该实体应被lambda表达式捕获。
根据[basic.def.odr]:
,如果变量是odr使用的话变量x的名称显示为可能评估的表达式ex,除了将lvalue-to-rvalue转换(4.1)应用于x得到常量表达式(5.20)之外,ex 使用odr这不会引起任何重要的琐事 函数和,如果x是一个对象,ex是表达式e的潜在结果集的一个元素,其中将左值到右值的转换(4.1)应用于e ,或者e是一个废弃值表达式(第5条)。
并且,来自[expr.const]:
条件表达式e是核心常量表达式,除非e的评估遵循规则 抽象机器(1.9),将评估以下表达式之一:[...]左值到右值的转换(4.1),除非它应用于整数或枚举类型的非易失性glvalue,指的是完全非具有前面初始化的易失性const对象,用常量表达式初始化
在:
return item == controlValue;
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
};
// ...
}