保存函数参数并稍后调用函数的最佳方法是什么?

What is the best way to save function arguments and call the function later?

在 C++14 中,我希望能够保存函数参数并在以后调用该函数。我的目标是能够做类似的事情:

auto delayed_function{ some_callable };

delayed_function( 1, 2, 3 );   // arguments are stored, but some_callable is not called yet
delayed_function.call();  // some_callable called with (1,2,3)

主要问题是我需要获取参数并存储它们。似乎将它们存储在 std::tuple 中效果最好。问题是元组的类型到底应该是什么。我的想法是,我需要在提供参数时推断出元组的类型(而不是试图从参数推断出可调用对象的类型),因为在提供参数时,它们可能是引用、右值等。 ,我只会从参数类型中知道这一点。然而,这意味着即使我知道可调用对象的类型,我也不知道元组的类型,除非在提供参数的模板方法中(我上面的示例中的 operator())。所以听起来我需要某种类型的擦除。我目前对参数存储的实现是:

template <typename Callable>
class arg_tuple
{
public:
  template <typename ... Args>
  arg_tuple( Args&&... args )
  {
    using TupleType = decltype( std::make_tuple(std::forward<Args>(args)...) );
    args_ =
      std::make_unique< typed_arg<TupleType> >(
        std::make_tuple(std::forward<Args>(args)...)
        );
  }

  void
  invoke( Callable f )
  {
    args_->invoke( f );
  }
private:

  template <typename T = void>
  struct untyped_arg {
    virtual ~untyped_arg() = default;

    virtual void invoke( Callable f ) = 0;
  };


  template <typename T>
  struct typed_arg
    : public untyped_arg<> {
    template <typename Arg>
    typed_arg( Arg&& arg )
      : value(std::forward<Arg>(arg) ) {}

    virtual void
    invoke( Callable f )
    {
      // By moving value, I can only call invoke once, but it potentially saves
      // copying, and I can use move-only types in the arg list.
      my_apply( f, std::move(value) );
    }

    T  value;
  };

  std::unique_ptr<untyped_arg<>>  args_;
};

其中“my_apply”本质上是 std::apply,但由于我使用的是 C++14,因此我不得不手动实现它。好消息是这基本上有效:

  auto  f = [](int i) { printf("i+3=%d\n", i+3); };
  using Callable = decltype(f);
  arg_tuple<Callable>  t(20);
  t.invoke(f);

然而,让我困扰的是 Callable 是 arg_tuple 的模板参数。我真的希望它成为调用方法的模板参数,而不是整个 class。我这样做的原因是 arg_tuple 不知道存储元组的类型,所以 invoke 需要一个虚函数才能找到元组类型已知的地方(typed_arg).然而,虚拟方法不能有模板参数,所以这就是我卡住的地方。

如果有一种方法可以使 Callable 成为调用的模板方法,那么就可以使它成为转发引用,这样我就不必按值接受它了。否则,我想我可能需要对左值、const 左值和右值引用进行多次调用重载,以防 Callable 是可变对象。

有更好的方法吗?

有几种方法可以做到这一点。

使用未来:

auto value = std::async(std::launch::deferred, delayed_function, 1, 2, 3);
...
value.get();

或者只使用 lambda:

auto f = [arg]() { return delayed_function(arg, 2, 3); }
...
f();