【问题标题】:C++ Lambdas, Capturing, Smart Ptrs, and the Stack: Why Does this Work?C++ Lambda、捕获、智能 Ptr 和堆栈:为什么会这样?
【发布时间】:2011-12-19 04:13:24
【问题描述】:

我一直在尝试 C++11 中的一些新功能,并尝试编写以下程序,但希望它不起作用。令我惊讶的是,它确实如此(在带有 'std=c++0x' 标志的 Linux x86 上的 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 的变量捕获将允许所有的词法作用域/闭包技巧,就像 Lisp 一样?我是否可以假设所有捕获的引用都将保持活动状态,直到 lambda 本身被删除,从而允许我以上述方式使用 smart_ptrs?

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

【问题讨论】:

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


    【解决方案1】:

    lambda 按值捕获from_ref,因此它会进行复制。因为这个副本,from_ref 被销毁时引用计数不是 0,而是因为 lambda 中仍然存在副本,所以它是 1。

    【讨论】:

    • 那么我理解 std::function 对象本身在整个实例期间存储捕获值是否正确?并且通过存储这个引用,shared_ptr 引用计数永远不会达到 0?啊,我明白了。多么优雅。
    • @Louis 不,不是 function 对象,而是 lambda。 std::function 不知道 lambdas 的捕获。
    • 因此,引用的捕获和存储作为一种特殊情况处理,而不是作为 std::function 实例中的成员存储。谢谢。
    • @Louis - 捕获的变量存储为编译器生成的 lambda 对象的成员变量。每个 lambda 表达式都会生成一个自定义类,该类将捕获存储为成员(通过引用或按值,具体取决于捕获模式),并具有与 lambda 主体等效的operator()。然后实例化这个类以创建 lambda 表达式的结果值。
    【解决方案2】:

    以下内容:

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

    【讨论】:

      【解决方案3】:

      from_ref 是按值捕获的。

      如果你替换,你的推理是有效的

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

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

      【讨论】:

        猜你喜欢
        • 2022-11-19
        • 2014-12-20
        • 2020-02-21
        • 2011-10-30
        • 1970-01-01
        • 2012-12-18
        • 2018-02-12
        • 1970-01-01
        相关资源
        最近更新 更多