【问题标题】:Are C++11 lambdas themselves subject to RAII automatic deallocation?C++11 lambda 本身是否受 RAII 自动释放?
【发布时间】:2013-11-13 18:28:07
【问题描述】:

我想编写一个可选地接受 lambda 以自定义其行为的类方法。所以在使用这个类时,我想知道我是否需要担心 lambda 本身超出范围?

lambda 不会引用任何外部变量,所以我不担心变量的范围,只担心 lambda 本身的范围,我会在类中存储一个引用。

我是否需要担心 lambda 本身的创建方式/位置?

【问题讨论】:

  • 我认为函数本身永远不会超出范围。 lambda 只是 Functor 对象的闪亮语法。它将归结为一个函数指针。不要持有对 lambda 的引用,而是保留一份副本。如果捕获列表为空,它会很便宜。
  • 一般来说,最好用代码示例来解决这类问题。
  • 它是一个自己独特的类类型的实例。它遵循与任何其他实例相同的对象生命周期规则。您是否需要对此“担心”完全取决于您如何使用它,而我们对此一无所知。
  • 函子通常应该按值处理,而不是按引用处理。 Lambda 表达式只是创建函子的一种便捷方式,因此您也应该将 lambda 的结果作为值来处理。

标签: c++ c++11 lambda raii


【解决方案1】:

在狭窄的情况下,引用不会延长您所引用事物的生命周期。

使用对生命周期已过的事物的引用是未定义的行为。

无状态 lambda 的未定义行为可能是“我什至不使用我的 this 指针”,所以你可能没问题。但是,如果您知道 lambda 将是无状态的,则可以改为存储函数指针。

现在,为了存储实际的 lambda,您的类必须在该 lambda 类型上进行模板化。如果它是一个无状态的 lambda,它几乎肯定会比对该 lambda 的引用一样小(或更小)。那么为什么不只存储 lambda 的副本呢?

如果您存储的是 std::function<void()> 或类似的,则不是对 lambda 的引用。那是一个类型擦除对象,它包装了一个 lambda 的副本。在超出范围后存储对std::function<void()> 的引用将是一个坏主意,因为它不是无状态的,并且在您尝试调用它时会关闭并读取垃圾内存。 p>

【讨论】:

    【解决方案2】:

    可能无法完全填写您的答案,但请看一下herb sutter 如何使用 lambdas 解决方案解决了类似的 RAII。另见SO Question

    template <class T> class locker {
    private:
      mutable T m_t; // Copies the lambda here.
      mutable std::mutex m_m;
    public:
      locker( T t = T{} ) : m_t(t) {}
      template <typename F>
      auto operator()(F f) const -> decltype(f(m_t)) {
        std::lock_guard<mutex> _{m_m};
        return f(t);
      }
    };
    
    
    // usage 
    locker<std::string> s;
    s([](string &s) {
      s += "foobar";
      s += "barfoo";
    });
    

    关于这个例子的重要部分是复制了 Lambda。您不应持有对 lambda 的引用,因为函数本身将位于程序的只读部分中。 lambda 保存的唯一数据是函数指针及其捕获。如果确实有捕获,则取决于 lambda 的范围,如果超出范围,您将访问已释放的内存。

    【讨论】:

    • Lambda 不保存函数指针:它们的 operator() 是它们类型的一部分,而不是它们的运行时状态。
    • @YakK:我会改写一下,我想说的是,你的编译器会生成跳转到标签的代码,而这个标签可能不在只读部分你的二进制文件。
    • 什么?嗯?从 C++11 开始,没有更多的理由让 lambda 的代码“不在二进制文件的只读部分”,而不是任何其他代码“不在二进制文件的只读部分”。 (C++1y 中有一些提议,比如指向有状态 lambda 调用的函数指针,可能需要类似的东西)。
    • OP 指的是“lambda 本身的范围”。唯一可能不在只读部分的是标签,它存储、复制或引用捕获列表中的变量。其他都是RO。并不是说您需要自修改代码或 ram 函数来实现可能泄漏或超出范围的 lambda。
    • @Alex:“跳转到标签”意味着标签在代码段中;要么是只读代码部分,要么是读/写代码部分。后者意味着自修改代码。你的 2 cmets 是矛盾的。
    猜你喜欢
    • 2012-07-26
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 2017-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多