c++ lambda捕获,捕获“未声明的变量”

omqzjyyz  于 2023-06-25  发布在  其他
关注(0)|答案(1)|浏览(231)

我对this C++ weekly中的一个片段感到惊讶。
我在这里复制了我不理解的部分:

  1. int main() {
  2. auto accumulator = [sum = 0](int value) mutable {
  3. sum += value;
  4. return sum;
  5. };
  6. accumulator(1); // expected return value: 1
  7. accumulator(2); // expected return value: 1+2
  8. return accumulator(-1); // expected return value: 1+2-1
  9. }

隐含的期望(在注解中)得到满足,但我不认识这个捕获语法:sum在lambda声明作用域中不存在,所以它不能捕获它。
我知道这种语法:[lhs = rhs](whatever){whatever_again};,在闭包中声明一个lhs变量,该变量在声明时由声明范围中的rhs值初始化。这段代码可以工作的事实似乎意味着:

  • 如果没有声明所请求的捕获变量,则捕获实际上是一个定义;
  • 调用之间的值的持久性,意味着sum实际上是lambda闭包的成员;
  • sum不是const,因为lambda是可变的。

然而,我无法将这些假设与lambda cppreference相匹配。
我知道这种语法:[lhs = rhs](whatever){whatever_again};,在闭包中声明一个lhs变量,该变量在声明时由声明范围中的rhs值初始化。上面的行为似乎表明rhs可以仅仅是一个值,而lhs的实际类型是自动推导出来的(我想我是从下面的ClosureType::Captures中得到这一点的)。* * 我的理解是否正确?有人能指出一个参考或解释cppreference的措辞吗?**
我担心它会引发第二个问题,这可能是一个重复的问题(请填写免费告诉我,或者要求我将帖子拆分为2):* * 如何确定lambda体中变量的类型?**

备注

来自ClosureType::捕获:lambda body中的类型。
每个数据成员的类型是对应的捕获实体的类型,除非实体具有引用类型(在这种情况下,对函数的引用被捕获为对被引用函数的左值引用,对对象的引用被捕获为被引用对象的副本)。
ClosureType::Captures:闭包非静态成员变量:
闭包类型包括以未指定的顺序声明的未命名的非静态数据成员,其保持如此捕获的所有实体的副本。
恕我直言,如果我将 * entity * 概念从lvalue扩展到任何类型的值,我的假设都可能是正确的。

w1jd8yoj

w1jd8yoj1#

隐含的期望(在注解中)得到满足,但我不认识这个捕获语法:sum不存在于lambda声明作用域中,因此它无法捕获它。
sum不需要。sum不是我们要捕获的实体的名称,它只是捕获子句中的一个名称,它是lambda的数据成员的别名,我们可以在调用操作符中使用它。只有当sum没有初始化器时,sum才是被捕获实体的名称。这个特性(带有初始化器的capture子句)被称为 Generalized Captures
如果我们将上面的代码片段转换为“引擎盖下”发生的事情,那么它将更有意义:

  1. int main() {
  2. // exposition-only, closure type is unnamed.
  3. struct __lambda {
  4. // exposition-only, data member is nameless.
  5. // lambda is mutable, so member is not const.
  6. int __sum;
  7. // return type will deduce to int.
  8. // mutable lambda means our call operator is not const-qualified.
  9. // call operator is automatically constexpr because it can be.
  10. constexpr auto operator()(int value) {
  11. __sum += value;
  12. return __sum;
  13. }
  14. };
  15. // We've used copy initialization in our capture, so copy initialization
  16. // must take place for the __sum member.
  17. // However, closure types are not aggregate types.
  18. auto accumulator = __lambda{.__sum = 0};
  19. accumulator(1);
  20. accumulator(2);
  21. return accumulator(-1);
  22. }

您可以看到捕获子句中的[sum = 0]仅仅意味着闭包类型中有一个类型为int的成员。
每个数据成员的类型都是对应的捕获实体的类型,除非该实体具有引用类型。

  • 参见cppreference
    在本例中,捕获的实体是临时对象0,其类型为int,因此我们的数据成员也是int类型。
    标签:Your code on cppinsights.io
展开查看全部

相关问题