【问题标题】:C++ lambda returning itselfC ++ lambda返回自身
【发布时间】:2019-02-09 22:16:34
【问题描述】:

我想编写一个返回自身的 lambda,所以我可以在现场多次调用它。但看起来 lambda 内部的this 不是指 lambda,而是指周围对象的 this,如果 lambda 是在成员函数内部定义的。

这是一个例子:

#include <iostream>

int main(int argc, char* argv[]) {
  int a = 5;
  [&](int b) {
    std::cout << (a + b) << std::endl;
    return *this;
  }(4)(6);
}

有没有办法做类似的事情?

【问题讨论】:

  • 创建一个旧的正则函子?
  • @Jarod42 谢谢。我忘了我们现在可以在函数中定义类了。
  • 相关:A Proposal to Add Y Combinator to the Standard Library。我引用:“C++11/14 lambdas 不鼓励递归:无法从 lambda 函数的主体中引用 lambda 对象。解决此问题的常见解决方法是使用 std::function”
  • @HolyBlackCat:写出std::function的类型参数有点困难,有。
  • @SU3:你总是可以,只是在 C++11 之前你不能使用本地类类型作为模板类型参数。 (这不是本案的障碍)

标签: c++ lambda


【解决方案1】:

使用旧仿函数:

int main() {
  int a = 5;
  struct S {
    const S& operator ()(int b) const {
      std::cout << (a + b) << std::endl;
      return *this;
    }
    const int& a;
  };
  S{a}(4)(6);
}

Demo

【讨论】:

    【解决方案2】:

    你不能返回 lambda 本身,但你可以返回一个不同的:

    #include <iostream>
    
    int main() {
      int a = 5;    
      [&](int b) {
          auto impl = [&](int b){std::cout << (a + b) << std::endl;};
          impl(b);
          return impl;
      }(4)(6);
    }
    

    但是,这只允许再调用一次。不确定是否有一些技巧可以从中获得更多...

    【讨论】:

    • 这很聪明。
    • @HolyBlackCat 看起来比实际更好。一个人只能调用一次或两次:/
    • “技巧”是一个定点组合器。请参阅我对这个问题的评论。
    【解决方案3】:

    Ben Voigt 建议使用 Y 组合器(这是对标准库的一个很好的建议,顺便说一句),但您的问题更简单。你可以引入一个小的模板仿函数来代替 lambda:

    template<typename T>
    struct recallable_impl {
        template<typename... Args>
        recallable_impl& operator()(Args&&... args) {
            f(std::forward<Args>(args)...);
            return *this;
        }
    
        template<typename F>
        recallable_impl(F&& f)
          :  f{std::forward<F>(f)}
        {}
    private:
        T f;
    };
    
    template<typename T>
    decltype(auto) recallable(T&& f) {
        return recallable_impl<std::decay_t<T>>(std::forward<T>(f));
    }
    

    您的 lambda 甚至不需要显式返回任何内容:

    int main() {
      int a = 5;  
      recallable([&](int b){std::cout << (a + b) << std::endl;})(4)(5)(6)(7)(8);
    }
    

    【讨论】:

    • 啊,我假设是 C++17。我将使用支持 C++14 的示例进行更新。
    • 不错。而且每次调用都是在同一个对象实例上进行的,所以如果 lambda 具有可变状态,它将正确传播。
    • 因为生命周期不匹配? IIRC,临时生命周期在它们所在的完整表达式的末尾结束,因此只要recallable_impl 不在本地存储,它就应该在这方面很好。
    • @jaggedSpire 感谢您提供的信息!尽管如此,从这个类的用户的角度来看,通过简单地存储一个对象来意外导致 UB 听起来像是一个很大的禁忌。
    【解决方案4】:

    如果将 lambda 分配给预定义类型(不是自动),则可以调用它,并且它会被捕获:

    std::function<void(int)> f =[&f](int n)
    {
        if (n>0) f(n-1);
        return;
    };
    

    【讨论】:

    • 虽然递归和返回 self 都需要从 lambda 内部命名 lambda,但它们仍然是不同的操作。递归在某种意义上“更容易”,因为返回类型(必须在 std::function 类型参数内命名)更简单。简单得多。
    • 是的。所以我必须声明一个返回类型的类型,它返回一个类型,...... OMG 一直是海龟类型;-)
    猜你喜欢
    • 2019-02-11
    • 1970-01-01
    • 2012-09-20
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    相关资源
    最近更新 更多