我需要创建一个非模板类¹,它需要保存一个可调用的及其参数,以便稍后调用它们。
由于这个类正在建模异步执行,我想给它提供与std::thread
/ std::async
匹配的API和语义:
class Async {
public:
template <typename Function, typename... Args >
explicit Async(Function&& f, Args &&... args)
{
// perform a (decay) copy of the args,
// store f and the copy somewhere
}
void call()
{
// to be possibly called in a different thread;
// call the function stored. no need to return the results
}
private:
// which data members?
};
我想到的第一件事是使用std::function<void()>
作为唯一的数据成员,并通过参数从std::bind
初始化它。
但是,这不起作用:std::function
和std::bind
不支持仅限移动类型,std::bind
根本不支持std::invoke
(并且它会做很多额外的魔术,比如递归解析绑定)。
我是否遗漏了标准库中可用的一些易于使用的解决方案来实现这一点,还是应该部署自己的绑定器和类型擦除的函数包装类?
¹长话短说:这个类需要使用虚方法从基类继承,因此将此类作为模板类会导致通常的代码膨胀和冲突,因为虚拟表和虚拟方法的重复出现在任何地方。 / p>
答案 0 :(得分:3)
template<class T> T&& wrap_pm(T&& t) { return std::forward<T>(t); }
template<class T, class U>
auto wrap_pm(U T::* p) -> decltype(std::mem_fn(p)) { return std::mem_fn(p); }
class Async {
public:
template <typename Function, typename... Args >
explicit Async(Function&& f, Args &&... args)
: fut(std::async(std::launch::deferred,
[f=std::forward<Function>(f)](auto&&... args) mutable
{
wrap_pm(std::move(f))(decltype(args)(args)...);
}, std::forward<Args>(args)...))
{ }
void call()
{
fut.get();
}
private:
std::future<void> fut;
};
我对效率没有任何承诺。
答案 1 :(得分:2)
技术上std::packaged_task<void()>
符合您的要求。这是严重的过度杀伤,因为它可以与线程交互,并可以产生未来。
我写了一个task
来处理仅限移动的调用,这非常简单。正确地进行小缓冲区优化会更难。
class Async {
public:
template <typename Function, typename... Args >
explicit Async(Function&& f, Args &&... args) {
task = [f=std::forward<Function>(f), args=std::make_tuple(std::forward<Args>(args)...)]()mutable{
std::apply(std::move(f), std::move(args));
};
}
void call() {
task();
task={}; // don't call twice
}
private:
std::packaged_task<void()> task;
};
我使用的是C ++ 17 std apply。只需重新实现它,使用谷歌查找实现。