C++ 隐式捕获模板lambda中的常量变量,但未指定捕获默认值

C++ 隐式捕获模板lambda中的常量变量,但未指定捕获默认值,c++,lambda,c++17,language-lawyer,c++20,C++,Lambda,C++17,Language Lawyer,C++20,考虑以下代码: auto f() { const auto x = 1; return [] (auto) { return x; }; } 。我应该信任哪个编译器?这是某个编译器扩展,Clang尚未实现,还是只是一个Clang bug?是的,Clang bug 适用规则来自: 如果一个本地实体在一个不能使用odr的作用域中使用odr,则该程序是格式错误的 值得注意的是,规则说“如果你使用odr,它的格式就不正确”,而不是“如果我们不能确定它是否是odr使用,它的格式就不正确” 在该函

考虑以下代码:

auto f() {
  const auto x = 1;
  return [] (auto) { return x; };
}
。我应该信任哪个编译器?这是某个编译器扩展,Clang尚未实现,还是只是一个Clang bug?

是的,Clang bug

适用规则来自:

如果一个本地实体在一个不能使用odr的作用域中使用odr,则该程序是格式错误的

值得注意的是,规则说“如果你使用odr,它的格式就不正确”,而不是“如果我们不能确定它是否是odr使用,它的格式就不正确”

在该函数调用操作符模板的任何专门化中都没有使用odr的
x

这是一个明显的错误

我们的规则是:

在以下情况下,本地实体在作用域中是odr可用的:

  • 本地实体不是*this,或者存在封闭类或非lambda函数参数作用域,如果最内部的作用域是函数参数作用域,则它对应于非静态成员函数,以及
  • 对于实体引入点和范围(其中*被认为是在最内层的封闭类或非lambda函数定义范围内引入)之间的每个中间范围([basic.scope.scope]),或者:
    • 中间作用域是块作用域,或
    • 中间作用域是lambda表达式的函数参数作用域,该表达式具有命名实体的简单捕获或具有捕获默认值,lambda表达式的块作用域也是中间作用域
如果一个本地实体在一个不能使用odr的作用域中使用odr,则该程序是格式错误的

在我们的例子中:

autof(){
常数自动x=1;
return[](自动){return x;};
}
x
在lambda主体中不可用odr,因为中间作用域不捕获
x
(既不是简单捕获,也不是捕获默认值)

所以,它不是odr可用的。但是否使用odr?不,来自:

如果表达式是表示变量的id表达式,则该变量由表达式命名。变量
x
的名称显示为可能计算的表达式
E
E
使用的odr,除非

  • x
    是在常量表达式([expr.const])中可用的引用,或
  • x
    是一个非引用类型的变量,在常量表达式中可用,并且没有可变的子对象,
    E
    是应用左值到右值转换([conv.lval])的非易失性限定非类类型表达式的一组潜在结果的元素,或者
  • x
    是非引用类型的变量,
    E
    是未应用左值到右值转换的废弃值表达式([expr.prop])的潜在结果集的一个元素
第二个项目符号适用(我们的变量甚至被命名为
x
!)
x
在常量表达式中可用,因为它是常量整数类型,
E
在这里是左值到右值的转换

因此,
x
不是odr可用的,也不是odr使用的,所以这里没有问题


事实上,我们甚至有这样一个例子:

void f(int,const int(&)[2]={});//#1.
void f(常数int&,常数int(&)[1]);//#2.
无效测试(){
常数int x=17;
自动g=[](自动a){
f(x);//确定:调用#1,不捕获x
};
}

如果变量具有常量非易失性整数或枚举类型,并且已使用常量表达式fyi初始化,则lambda表达式可以读取变量的值,而无需捕获该值-小更改,并编译
return[](auto y){return x+y;}
可能会添加
[语言律师]
?另一件事,为什么
gcc
会抛出警告(
变量'x'设置但未使用[-Wunused但设置变量]
),即使它在lambda内部使用?可能相关:甚至更奇怪的clang编译
返回[](自动){return x;}感谢您的详细描述。我已经提交了一份bug报告。