c++ lambda, capture, Smart Ptrs和Stack:为什么这样做?

C++ Lambdas, Capturing, Smart Ptrs, and the Stack: Why Does this Work?

本文关键字:为什么 这样做 Stack Ptrs lambda capture Smart c++      更新时间:2023-10-16

我一直在摆弄c++ 11中的一些新特性,我试图编写以下程序,希望它不能工作。令我惊讶的是,它确实(在Linux x86上的GCC 4.6.1上使用'std=c++0x'标志):

#include <functional>
#include <iostream>
#include <memory>
std::function<int()> count_up_in_2s(const int from) {
    std::shared_ptr<int> from_ref(new int(from));
    return [from_ref]() { return *from_ref += 2; };
}
int main() {
    auto iter_1 = count_up_in_2s(5);
    auto iter_2 = count_up_in_2s(10);
    for (size_t i = 1; i <= 10; i++)
        std::cout << iter_1() << 't' << iter_2() << 'n'
        ;
}

我期望'from_ref'在每次执行返回的lambda运行时被删除。以下是我的推理:一旦count_up_in_2s运行,from_ref就会从堆栈中弹出,但是因为返回的lambda并不一定会立即运行,因为它返回了,所以在很短的一段时间内不会存在另一个引用,直到当lambda实际运行时,相同的引用被推回,所以shared_ptr的引用计数不应该为零,然后删除数据吗?

除非c++ 11的lambda捕获比我认为的要聪明得多,如果是的话,我会很高兴的。如果是这样的话,我是否可以假设c++ 11的变量捕获将允许所有的词法作用域/闭包技巧,就像Lisp一样,只要/something/正在处理动态分配的内存?我是否可以假设所有捕获的引用都将保持存活,直到lambda本身被删除,从而允许我以上述方式使用smart_ptrs ?

如果这是我认为是,这是不是意味着c++ 11允许表达高阶编程?如果是这样,我认为c++ 11委员会做得很好=)

lambda按值捕获from_ref,因此它创建一个副本。因为这个拷贝,当from_ref被销毁时,refcount不是0,它是1,因为拷贝仍然存在于lambda中。

以下内容:

std::shared_ptr<int> from_ref(new int(from));
return [from_ref]() { return *from_ref += 2; };

大致相当于:

std::shared_ptr<int> from_ref(new int(from));
class __uniqueLambdaType1432 {
  std::shared_ptr<int> capture1;
 public:        
  __uniqueLambdaType1432(std::shared_ptr<int> capture1) :
    capture1(capture1) { 
  }
  decltype(*capture1 += 2) operator ()() const {
    return *capture1 += 2;
  }
};
return __uniqueLambdaType1432(from_ref);

,其中__uniqueLambdaType1432是程序全局唯一类型,区别于任何其他类型,甚至是由词法相同的lambda表达式生成的其他lambda类型。它的实际名称对于程序员来说是不可用的,并且除了由原始lambda表达式产生的对象之外,不能创建它的其他实例,因为构造函数实际上被编译器隐藏了。

from_ref按值捕获

如果你用

代替
return [from_ref]() { return *from_ref += 2; };

return [&from_ref]() { return *from_ref += 2; };