从内部替换std :: function(通过move-assignment到* this?)

时间:2018-05-28 14:38:34

标签: c++ c++11 move-semantics std-function this-pointer

是否可以将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(); }

以下代码无法编译:

func

可以修改编译吗? 现在的错误消息是:&#39; this&#39;没有捕获这个lambda函数 - 我完全理解。但是,我不知道如何捕捉this std::function指针。我猜,它甚至不是lambda里面的std::function,但是?!怎么办呢?

背景:我想要实现的目标如下:在给定sed -e 's/ENGINE=InnoDB/& ROW_FORMAT=COMPRESSED/' dump.sql | mysql ... 的第一次调用中,我想做一些初始化工作,然后用优化的原始函数替换一。我想为我的功能用户透明地实现这一点。

上述示例的预期输出为:

  

一个
  b
  B'/ P>

2 个答案:

答案 0 :(得分:5)

你不能在lambda中使用this来引用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比它的范围更长(比如通过值从函数返回它)而不先调用它(有效地重新分配存储的函数对象),那么你将获得一个悬空引用。 / p>

  

我猜,它甚至不是lambda中的std::function,但是?!

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

答案 1 :(得分: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组合器比在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)};
}

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

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

我们需要的是一种非常难以命名的类型 - F = std::function<void(F)> - 一个std::function,它将一个参数作为同一类型对象的实例。

通常没有办法做到这一点,但之前有一些模板tomfoolery ... Here, I did it

然后您的代码为:

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

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

Live example

此代码中的

selfrecursive_func< void(own_type&) >,因此您可以在b-printer中执行与在a-printer中相同的操作。