4

是否可以用另一个替换一个std::function从自身内部std::function

以下代码无法编译:

#include <iostream>
#include <functional>

int main()
{
    std::function<void()> func = []()
    {
        std::cout << "a\n";
        *this = std::move([]() { std::cout << "b\n"; });
    };
    func();
    func();
    func();
}

可以修改编译吗?
现在的错误消息是:没有为这个 lambda 函数捕获“this” ——我完全理解。但是,我不知道如何捕获func's this-pointer。我想,它甚至不是std::functionlambda 内部的,但是?!如何才能做到这一点?

背景:我想要实现的是:在给定的第一次调用中std::function,我想做一些初始化工作,然后用优化的函数替换原始函数。我想为我的功能的用户透明地实现这一点。

上述示例的预期输出为:

a
b
b

4

2 回答 2

5

您不能this在 lambda 内部使用来引用 lambda。this只会引用封闭类,在你的情况下没有,所以你不能使用this. 但是,您可以做的是捕获func并重新分配:

std::function<void()> func = [&func]()
{
    std::cout << "a\n";
    func = []() { std::cout << "b\n"; }; // note the missing move, a lambda
                                         // is already an rvalue
};

但是请注意,如果您func在不首先调用它(有效地重新分配存储的函数对象)的情况下让其范围(例如通过按值从函数返回),那么您将获得一个悬空引用。

我想,它甚至不是std::functionlambda 内部的,但是?!

它实际上是。名称在其声明符之后进入范围,因此在=,func类型std::function<void()>被引入之前。因此,在您引入 lambda 时,您已经可以捕获func.

于 2018-05-28T15:00:10.420 回答
3

这在技术上解决了您的问题:

std::function<void()> func = [&func]{
  std::cout << "a\n";
  func = []{ std::cout << "b\n"; };
};

但这不是一个好的计划。std 函数的生命周期和行为与本地堆栈变量相关联。副本几乎在任何意义上都无法完成您可能想要的操作——"a\b"除非由于原件func超出范围而出现段错误,否则它们会继续打印。


为了解决这个问题,我们必须部署一些大炮;首先,函数式编程女王 Y Combinator 女士:

std::function<void()> func = y_combinate( [](auto&& self){
  std::cout << "a\n";
  self = []{ std::cout << "b\"; };
} );

Y Combinator 接受一个签名函数F = (F,Args...)->R,然后返回一个签名函数(Args...)->R。当您在获得名称之前无法命名自己时,这就是无状态语言管理递归的方式。

编写 Y Combinator 比在 C++ 中担心的要容易:

template<class F>
struct y_combinator_t {
  F f;
  template<class...Args>
  auto operator()(Args&&...args)
  -> typename std::result_of< F&( F&, Args&&... ) >::type
  {
    return f( f, std::forward<Args>(args)... );
  }
};
template<class F>
y_combinator_t<typename std::decay<F>::type> y_combinate( F&& f ) {
  return {std::forward<F>(f)};
}

遗憾的是,这不起作用,因为self传递给 lambda 的类型实际上是原始 lambda 的类型。而 b-printing lambda 是一种不相关的类型。因此,当您尝试self = []{ std::cout << "b\n"; }在一些相对较深的模板垃圾邮件错误中嵌入错误时。

伤心; 但只是暂时的挫折。

我们需要的是一种很难命名的类型——a F = std::function<void(F)>——a std::function,它的一个参数是一个相同类型的对象的实例。

通常没有办法做到这一点,但有点模板愚蠢......在这里,我以前做过

然后你的代码如下:

std::function<void()> func = y_combinate( recursive_func< void(own_type&) >([](auto&& self){
  std::cout << "a\n";
  self = [](auto&&){ std::cout << "b\n"; };
}) );

并调用给定的func第一个 prints副本"a\n",然后每个后续调用 prints "b\n"。b 打印机的副本也打印 b,但 a 打印机的副本将在转换前第一次打印 a。

活生生的例子

self在此代码中是 a recursive_func< void(own_type&) >,因此您可以在 b 打印机中执行与在 a 打印机中相同的操作。

于 2018-05-28T17:57:59.020 回答