Pet*_*lev 7 c++ c++11 c++14 c++17
所以我正在尝试创建一个具有不同类型的仿函数容器的类.
这是它的简化版本.
template<class T>
class Container
{
public:
template<typename F, typename ... ARGS>
void addTask(F && func, ARGS && ... args);
private:
std::deque<std::function<T()>> container;
//.....
};
template<class T>
template<typename F, typename ... ARGS>
T Container<T>::addTask(F && func, ARGS && ... args);
{
container.emplace_back(std::bind(f,args...));
//.....
}
Run Code Online (Sandbox Code Playgroud)
还有一些我无法解决的问题.
std::bind和存储不同的对象或指针?int,void...)中返回不同对象的函数吗?consexprbind 这样的东西.来自 OP 的评论。
有。这是简化的。我在真实代码中使用期货和一个特殊的容器。它旨在用于多线程环境
这称为埋葬lede。
如果要存储要在其他线程中调用的可调用对象,则在其他线程中需要签名void()。在此线程中,您希望std::future填充 a。
至于绑定参数,虽然有很多std函数会为你做这件事,但我发现最好请求带有预绑定参数的可调用对象。他们可以在外面使用std::bindlambdas 或他们选择的任何其他方式来完成。
那么这就来了
template<class Func,
class R = std::decay_t<std::result_of_t<Func const&()>>
>
std::future< R >
addTask( Func&& func ) {
auto task = std::packaged_task<R()>(std::forward<Func>(func));
auto ret = task.get_future();
container.push_back( std::packaged_task<void()>( std::move(task) ) );
return ret;
}
std::deque< std::packaged_task<void()> > container;
Run Code Online (Sandbox Code Playgroud)
加入一些互斥体并摇晃和烘烤。
在这里,我将std::packaged_task<void()>带有该签名的任何内容用作预先编写的仅移动类型擦除容器。我们不使用future它可以产生的,这是一种浪费,但它比编写自己的 move-only invoke-once 拥有函数对象更短。
我个人只是给自己写了一个轻量级的仅移动std::function<void()>式的类而不是使用std::packaged_task<void()>,但这可能是不明智的。
addTask当packaged_task<R()>调用 时,从 返回的未来会被填满,当调用 时packaged_task<void()>调用(可能在另一个线程中)。
在结构之外,调用者可以给你任何零参数的可调用对象。
100 次中有 99 次,简单[some_arg]{ some_code; }甚至[]{ some_code; }有效。在复杂的情况下,他们可以std::bind使用更复杂的 lambda来处理或 C++14 改进。
将参数的存储放入addTask混合了线程任务队列的责任和参数的混乱。
事实上,我会与线程池分开编写一个线程安全队列,并让线程池使用它:
template<class T>
struct thread_safe_queue;
struct thread_pool {
thread_safe_queue< std::packaged_task<void()> > queue;
// etc
};
Run Code Online (Sandbox Code Playgroud)
在 C++17 中,您的绑定的替代品如下所示:
[
func = std::forward<Func>(func),
args = std::make_tuple( std::forward<Args>(args)... )
]() mutable {
std::apply( func, std::move(args) );
}
Run Code Online (Sandbox Code Playgroud)
在 C++14 中,您可以notstd::apply非常轻松地编写代码。Move-into-lambda 需要 C++14,所以如果你需要有效地移动参数,你需要 std bind 或 C++11 中的手动函数对象。
我认为最好使用线程池将参数绑定强烈地放在代码域中。
这也允许线程池执行诸如传递任务可选的额外参数之类的事情,例如“取消令牌”等。
std::bind来自boost::bind,我们有lambda之前是必要的.
不幸的是std::bind,它与lambda一起成为标准,因此它几乎无关紧要.
在c ++ 14及更高版本中,您可以捕获可变参数lambda中的函数和参数:
template<class T>
template<typename F, typename ... ARGS>
T Container<T>::addTask(F && func, ARGS && ... args)
{
container.emplace_back( [func = std::forward<F>(func),
args...]
() mutable // make mutable if you want to move the args in to func
{
return func(std::move(args)...);
});
//.....
}
Run Code Online (Sandbox Code Playgroud)
你不会以这种方式完美转发.捕获中隐含有一个副本args...
这在c ++ 17中得到了解决
template<class T>
template<typename F, typename ... ARGS>
T Container<T>::addTask(F && func, ARGS && ... args)
{
container.emplace_back( [func = std::forward<F>(func),
args = std::make_tuple(std::forward<ARGS>(args)...) ]
() mutable // make mutable if you want to move the args in to func
{
return std::apply(func, std::move(args));
});
//.....
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2085 次 |
| 最近记录: |