C++ 为什么有时不需要在lambda中捕获常量变量?
考虑以下示例:C++ 为什么有时不需要在lambda中捕获常量变量?,c++,lambda,constants,language-lawyer,C++,Lambda,Constants,Language Lawyer,考虑以下示例: #include <cstdlib> int main() { const int m = 42; [] { m; }(); // OK const int n = std::rand(); [] { n; }(); // error: 'n' is not captured } #包括 int main(){ 常数int m=42; []{m;}();//确定 常量int n=std::rand(); []{n;}();//错误
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
#包括
int main(){
常数int m=42;
[]{m;}();//确定
常量int n=std::rand();
[]{n;}();//错误:未捕获“n”
}
为什么我需要在第二个lambda中捕获n
,而在第一个lambda中不捕获m
?我查看了C++14标准中的第5.1.2节(Lambda表达式),但找不到原因。你能给我指一段解释这一点的话吗
更新:我在GCC6.3.1和7(主干)中都观察到了这种行为。Clang 4.0和5(trunk)在这两种情况下都会失败,并出现错误(
变量“m”不能在没有指定捕获默认值的lambda中隐式捕获)。由于它是一个常量表达式,编译器将其视为[]{42;}()代码>
[]中的规则是:
如果lambda表达式或函数调用的实例化
通用lambda odr的运算符模板使用(3.2)此或
具有自动存储持续时间的变量,
该实体应由lambda表达式捕获
这里引用标准[]:
一个名为x的变量
显示为可能计算的表达式ex是否使用odr,除非
将左值到右值的转换应用于x会产生一个常量表达式(…),或者e是一个废弃的值表达式
(删除了不太重要的部分以保持简短)
我的简单理解是:编译器知道m
在编译时是常数,而n
在运行时会发生变化,因此必须捕获n
n
将使用odr,因为您必须在运行时查看n
中的内容。换句话说,n
的“只能有一个”定义是相关的
这是M.M.的评论:
m是常量表达式,因为它是常量自动变量
使用常量表达式初始值设定项,但n不是常量
表达式,因为其初始值设定项不是常量表达式。这
参见[expr.const]/2.7。常量表达式不是
根据[basic.def.ODR]/3的第一句话,使用了ODR
请参见此处以获得一个。编辑:我以前的答案是错误的。初学者的答案是正确的,以下是相关的标准报价:
如果变量x的名称显示为可能计算的表达式ex,则该变量x由ex使用odr,除非将转换应用于x会产生一个不调用任何非平凡函数的变量,并且如果x是一个对象,则ex是表达式e的一组可能结果的元素,其中左值到右值的转换应用于e,或者e是一个废弃的值表达式李>
由于m
是一个常量表达式,因此不使用odr,因此不需要捕获
似乎clangs行为不符合标准。对于块范围内的lambda,在到达范围内满足某些标准的变量可以在lambda内以有限的方式使用,即使它们没有被捕获
粗略地说,到达作用域包括包含lambda的函数的任何局部变量,该变量在定义lambda时位于作用域中。因此,在上述示例中,这包括m
和n
“特定标准”和“有限方式”具体如下(从C++14开始):
- 在lambda内部,不得使用odr变量,这意味着它不得进行任何操作,除非:
- 显示为丢弃的值表达式(
m;
是其中之一),或
- 有其价值的
- 变量必须为:
- 初始值设定为常量表达式的
常量
、非易失性
整数或枚举,或
- 一个
constepr
、非volatile
变量(或此类变量的子对象)
参考C++14:[expr.const]/2.7、[basic.def.odr]/3(第一句),[expr.prim.lambda]/12、[expr.prim.lambda]/10
这些规则的基本原理,如其他注释/答案所示,是编译器需要能够“合成”一个不捕获lambda作为独立于块的自由函数(因为这些东西可以转换为函数指针);如果它知道变量将始终具有相同的值,那么它可以在引用变量的情况下执行此操作,或者它可以重复此过程以获得独立于上下文的变量值。但是,如果变量可能随时不同,或者例如需要变量的地址,它就不能这样做
在代码中,n
由一个非常量表达式初始化。因此,n
不能在lambda中使用而不被捕获
m
是由常量表达式42
初始化的,因此它确实符合“特定标准”。丢弃的值表达式不使用odr表达式,因此m代码>可以在不捕获m
的情况下使用。gcc是正确的
我想说这两个编译器之间的区别是,clang考虑m代码>到odr使用m
,但gcc不使用。[basic.def.odr]/3的第一句话相当复杂:
变量x
的名称显示为可能计算的表达式ex
是ex
使用的odr,除非将左值到右值的转换应用到x
生成一个常量表达式,该表达式不会调用任何非平凡函数,并且如果x
是一个对象,ex
是表达式e
的潜在结果集的一个元素,其中左值到右值的转换应用于e
,或者e
是一个废弃的值表达式
b