Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么有时不需要在lambda中捕获常量变量?_C++_Lambda_Constants_Language Lawyer - Fatal编程技术网

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