s3r*_*vac 70 c++ lambda const language-lawyer
请考虑以下示例:
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
Run Code Online (Sandbox Code Playgroud)
为什么我需要捕获n第二个lambda而不是m第一个lambda?我在C++ 14标准中检查了第5.1.2节(Lambda表达式)但我无法找到原因.你能指点我解释一个段落吗?
更新:我在GCC 6.3.1和7(主干)中观察到了这种行为.Clang 4.0和5(主干)在两种情况下都失败并出错(variable 'm' cannot be implicitly captured in a lambda with no capture-default specified).
M.M*_*M.M 50
对于在块范围的λ,在满足特定准则的变量达到范围可以在lambda内部有限方式被使用,即使它们没有捕获.
粗略地说,达到范围包括包含lambda的函数的局部变量,它将在lambda定义的范围内.因此,这包括m与n在以上示例.
"特定标准"和"有限方式"是具体的(从C++ 14开始):
m;是其中之一),或const,非volatile整数或枚举,其初始化程序是常量表达式,或constexpr,非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,因此它符合"特定标准".丢弃值表达式不会使用表达式,因此m;可以在不m被捕获的情况下使用.gcc是对的.
我会说两个编译器之间的区别在于clang考虑m;使用odr m,但gcc没有.[basic.def.odr]/3的第一句话非常复杂:
一种可变
x为潜在评估表达式出现的名字ex被ODR使用的由ex除非施加左值到右值转换到x产率的常量表达式不调用任何非平凡函数,并且如果x是一个对象,ex是的一个元素表达式的潜在结果集e,其中应用了左值到右值的转换e,或者e是丢弃值表达式.
但仔细阅读后,它确实特别提到丢弃值表达式并没有使用表达式.
C++ 11的[basic.def.odr]版本最初没有包含丢弃值表达式的情况,因此clang的行为在已发布的C++ 11下是正确的.但是,C++ 14中出现的文本被认为是对C++ 11的缺陷(问题712),因此编译器应该更新它们的行为,即使在C++ 11模式下也是如此.
Beg*_*ner 33
因为它是一个常量表达式,编译器就像是一样 [] { 42; }();
[ expr.prim.lambda ]中的规则是:
如果lambda表达式或函数的实例化调用泛型lambda odr的操作符模板 - 使用(3.2)此变量或具有自其存储持续时间的变量,则该实体应由lambda表达式捕获.
这里引用标准[ basic.def.odr ]:
除非将lvalue-to-rvalue转换应用于x得到常量表达式(...)或e是丢弃值表达式,否则名称显示为可能已评估的表达式ex的变量x将被使用.
(删除不是那么重要的部分,以保持简短)
我的简单理解是:编译器知道它m在编译时是常量,而n在运行时会改变,因此n必须被捕获.n会被使用,因为你必须实际看一下n运行时的内部.换句话说,"只有一个"定义的事实n是相关的.
这来自MM的评论:
m是常量表达式,因为它是具有常量表达式初始值设定项的const自动变量,但是n不是常量表达式,因为它的初始值设定项不是常量表达式.这在[expr.const] /2.7中有所介绍.根据[basic.def.odr]/3的第一句,常量表达式不是ODR使用的
请参阅此处获取演示.
| 归档时间: |
|
| 查看次数: |
4898 次 |
| 最近记录: |