提问人:Oersted 提问时间:6/16/2023 最后编辑:Jan SchultkeOersted 更新时间:6/17/2023 访问量:100
lambda 捕获,捕获“未声明的变量”
lambda capture, capturing an "undeclared variable"
问:
我对这个C++周刊中的一个片段感到惊讶。
我在这里复制了我不明白的部分:
int main() {
auto accumulator = [sum = 0](int value) mutable {
sum += value;
return sum;
};
accumulator(1); // expected return value: 1
accumulator(2); // expected return value: 1+2
return accumulator(-1); // expected return value: 1+2-1
}
隐式期望(在注释中)已实现,但我不认识此捕获语法:lambda 声明范围内不存在 sum,因此它无法捕获它。
我知道这种语法:它在闭包中声明一个变量,该变量在声明时由声明范围中的值初始化。
此代码正在工作的事实似乎意味着:[lhs = rhs](whatever){whatever_again};
lhs
rhs
- 如果未声明请求的捕获变量,则捕获实际上是一个定义;
- 调用之间值的持久性意味着它实际上是 lambda 闭包的成员;
sum
sum
不是 const,因为 lambda 是可变的。
然而,我无法将这些假设与 lambda cpppreference 相匹配。
我知道这种语法:它在闭包中声明一个变量,该变量在声明时由声明范围中的值初始化。
上述行为似乎表明它可能只是一个值,并且自动推断出实际类型(我认为我从 ClosureType::Captures 中正确地理解了这一点,下面转载)。我的理解正确吗?有人可以指出参考或解释 cppreference 的措辞吗?
恐怕它提出了第二个问题,这可能是重复的(自由填写以告诉或要求我将帖子分成 2 个):如何在 lambda 主体内确定变量的类型?[lhs = rhs](whatever){whatever_again};
lhs
rhs
rhs
lhs
笔记
from ClosureType::Captures: 在 lambda 正文中键入。
每个数据成员的类型是相应捕获实体的类型,除非该实体具有引用类型(在这种情况下,对函数的引用将捕获为对引用函数的左值引用,而对对象的引用将捕获为引用对象的副本)。
也来自 ClosureType::Captures: closure 非静态成员变量:
闭包类型包括未命名的非静态数据成员,这些成员以未指定的顺序声明,这些成员包含如此捕获的所有实体的副本。
恕我直言,如果我将实体概念扩展到任何类型的值,我的假设可能是正确的。lvalue
答:
隐式期望(在注释中)已实现,但我不认识此捕获语法:在 lambda 声明范围内不存在,因此它无法捕获它。
sum
sum
不必如此。 不是我们正在捕获的实体的名称,它只是 capture 子句中的一个名称,它是 lambda 数据成员的别名,我们可以在调用运算符中使用。仅当没有初始值设定项时,捕获的实体的名称也可用。
此功能(带有初始值设定项的捕获子句)称为广义捕获。sum
sum
sum
如果我们将上面的代码片段转换为“引擎盖下”发生的事情,则会更有意义:
int main() {
// exposition-only, closure type is unnamed.
struct __lambda {
// exposition-only, data member is nameless.
// lambda is mutable, so member is not const.
int __sum;
// return type will deduce to int.
// mutable lambda means our call operator is not const-qualified.
// call operator is automatically constexpr because it can be.
constexpr auto operator()(int value) {
__sum += value;
return __sum;
}
};
// We've used copy initialization in our capture, so copy initialization
// must take place for the __sum member.
// However, closure types are not aggregate types.
auto accumulator = __lambda{.__sum = 0};
accumulator(1);
accumulator(2);
return accumulator(-1);
}
您可以看到,在我们的 capture 子句中,仅仅意味着我们在闭包类型中有一个 type 的成员。[sum = 0]
int
每个数据成员的类型是相应捕获实体的类型,除非该实体具有引用类型。
- 参见 cppreference
在这种情况下,捕获的实体是 的临时对象 ,它的类型是 ,所以我们的数据成员也是 类型 。0
int
int
Смотритетакже: 您在 cppinsights.io 上的代码
评论
rhs
int n = ...; int& m = n; [m](){};
m
m
n
m
sum
struct accumulator_t { int sum{ 0 }; int operator()(int value) { sum += value; return sum; } };