C ++ Lambdas,捕获,智能Ptrs和堆栈:为什么这有效?

时间:2011-12-19 04:13:25

标签: c++ variables memory-management lambda c++11

我一直在玩C ++ 11中的一些新功能,我尝试编写以下程序,期望它不起作用。令我惊讶的是,它(在Linux x86上使用'std = c ++ 0x'标志的GCC 4.6.1上):

#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'
        ;
}

我希望在每次执行返回的lambda时都会删除'from_ref'。这是我的推理:一旦运行count_up_in_2s,from_ref就会从堆栈中弹出,但因为返回的lambda不是必须立即运行,因为它已经返回,所以在相同的引用之前,没有另一个引用存在很短的时间当lambda实际运行时推回,所以shared_ptr的引用计数不应该为零然后删除数据吗?

除非C ++ 11的lambda捕获比我给它的功能更加聪明,如果是的话,我会很高兴。如果是这种情况,我可以假设只要/ something /处理动态分配的内存,C ++ 11的变量捕获将允许所有词法范围/闭包技巧la Lisp吗?我可以假设所有捕获的引用都会保留,直到lambda本身被删除,允许我以上述方式使用smart_ptrs吗?

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

3 个答案:

答案 0 :(得分:7)

lambda按值捕获from_ref,因此它会复制。由于此副本,from_ref被销毁时引用计数不为0,因为lambda中仍存在副本,因此它为1。

答案 1 :(得分:4)

以下内容:

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表达式产生的对象外,不能创建其它实例,因为构造函数实际上是用编译器魔法隐藏的。

答案 2 :(得分:1)

from_ref按值捕获。

如果您替换

,您的推理会有效
return [from_ref]() { return *from_ref += 2; };

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