为什么 Visual C++(最新)和 gcc 12.1 接受隐藏 lambda 的 init 捕获,而 clang 14.0.0 不接受?(c++20)

Man*_*uel 6 c++ lambda compiler-errors language-lawyer

情况1

int main() {
    int x = 100;
    auto lamb_var = [y = x](){
                    int y = 10;
                    return y + 1;
    }; 

    assert (lamb_var() == 11);

    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/hPPParjnz

MSVC 和 GCC 都接受隐藏 init-capture,而 Clang 则指责复合语句上的 y 重新定义并引发编译器错误。

但是,如果我们删除 init-capture 并进行 simple-capture,则所有编译器都接受 Shadowing:

案例2

int main() {
    int x = 100;
    auto lamb_var = [x](){
                    int x = 10;
                    return x + 1;
    }; 

    assert (lamb_var() == 11);

    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/Gs4cadf5e


简单捕获(情况 2)会导致在 lambda 关联类中创建属性,因此阴影应该是正常的。

根据我的发现,下面引用 cppreference 的表达式“其声明区域是 lambda 表达式的主体”可以捍卫 CLANG 的实现(“重新定义”),但我不确定。

使用初始值设定项的捕获的行为就像它声明并显式捕获使用类型 auto 声明的变量一样,其声明区域是 lambda 表达式的主体(即,它不在其初始值设定项的范围内),除了: [.. .]

谁在实现上是正确的(GCC 和 MSVC 还是 Clang),以及如何理解 cppreference 的引用?


相关问题

Lambda 捕获和参数同名 - 谁隐藏了另一个?(clang 与 gcc)

use*_*570 5

我认为clang 拒绝片段 1 和接受片段 2 是正确的,因为在第一种情况下,非静态数据成员被命名 y,而在第二种情况下,非静态数据成员是unnamed

情况1

这里我们考虑片段 1:

int main() {
    int x = 100;
    auto lamb_var = [y = x](){  //the data member is "named" y
                    int y = 10; //error because we're defining y for second time 
                    return y + 1;
    }; 

    assert (lamb_var() == 11);

    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

现在,来自expr.prim.lambda#capture-6

不带省略号的 init-capture 的行为就好像它声明并显式捕获其声明区域是 lambda 表达式的 复合语句形式的变量 ,但以下情况除外:auto init-capture ;

(强调我的)

这似乎表明非静态数据成员的名称在您给定的示例中为y。现在,通过编写,int y = 10;我们y在同一声明区域中提供了对同一命名变量的重新定义,从而导致了错误。


[y=x]请注意,如果我们用 和[x=x]with替换,我们将得到相同的错误(由于上述原因,正如预期的那样),int y =10;如下int x = 10; 所示:

int main() {
    int x = 100;
    auto lamb_var = [x = x](){  //data member "named" x
                    int x = 10; //this will give same error 
                    return x + 1;
    }; 

    assert (lamb_var() == 11);

    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

案例2

这里我们考虑片段 2:

int main() {
    int x = 100;
    auto lamb_var = [x](){         //data member is unnamed
                    int x = 10;   //ok because we're defining an int variable with "name" x for the first time in this region
                    return x + 1;
    }; 

    assert (lamb_var() == 11);

    return 0; 
}
Run Code Online (Sandbox Code Playgroud)

来自expr.prim.lambda#capture-10

对于复制捕获的每个实体,在闭包类型中声明一个未命名的非静态数据成员。这些成员的声明顺序未指定......

(强调我的)

在这种情况下,非静态数据成员是未命名的,因此写入int x = 10;不是重新定义错误,因为我们正在该区域中第一次定义一个命名的变量。 x