std::bind in Modern C++ 的替代方案
Alternative for std::bind in modern C++
所以我正在尝试创建一个类,该类具有不同类型的函子容器。
这是它的简化版本。
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...));
//.....
}
还有一些问题我不能解决。
- 有没有办法删除
std::bind
并存储不同的对象或指针? - 这可以更通用吗?我可以以某种方式将返回不同对象的函数存储在单个容器中(
int
、void
...... - 创建任务的某些逻辑是否可以在编译时执行?像
consexpr
绑定的东西。
来自 OP 的评论。
有。这是简化的。我在真实代码中使用期货和一个特殊的容器。它旨在用于多线程环境
这叫埋葬乐德。
如果要存储要在其他线程中调用的可调用对象,则需要在另一个线程中签名void()
。在此线程中,您希望填充std::future
。
至于绑定参数,虽然许多std
函数为您执行此操作,但我发现最好要求带有预绑定参数的可调用对象。 他们可以在外面做,使用std::bind
或lambdas或他们选择的任何其他方式。
所以这就来了
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;
加入一些互斥锁,摇晃和烘烤。
在这里,我将std::packaged_task<void()>
用作预先编写的仅移动类型擦除容器,用于具有该签名的任何内容。 我们不使用它可以产生的future
,这是一种浪费,但它比编写自己的仅移动调用一次拥有函数对象要短。
我个人只是给自己写了一个轻量级的动作式std::function<void()>
式类,而不是使用std::packaged_task<void()>
,但这可能是不明智的。
从addTask
返回的未来在调用packaged_task<R()>
时被填满,在调用packaged_task<void()>
时调用(可能在另一个线程中)。
在结构之外,调用方可以为您提供任何零参数可调用对象。
100次中有99次,一个简单的[some_arg]{ some_code; }
甚至[]{ some_code; }
有效。 在复杂的情况下,他们可能会使用更复杂的 lambda 进行std::bind
或 C++14 改进。
将参数的存储放入addTask
将线程任务队列的责任与弄乱参数混为一谈。
事实上,我会与线程池分开编写一个线程安全队列,并让线程池使用它:
template<class T>
struct thread_safe_queue;
struct thread_pool {
thread_safe_queue< std::packaged_task<void()> > queue;
// etc
};
在 C++17 中,绑定的替换如下所示:
[
func = std::forward<Func>(func),
args = std::make_tuple( std::forward<Args>(args)... )
]() mutable {
std::apply( func, std::move(args) );
}
在 C++14 中,您可以轻松编写notstd::apply
。 移动到 lambda 需要 C++14,因此如果您需要有效地移动参数,则需要 std 绑定或 C++11 中的手动函数对象。
我认为最好使用线程池将参数绑定强放在代码域中。
这也允许线程池执行诸如传递任务可选额外参数之类的操作,例如"取消令牌"等。
std::bind
来自boost::bind
,这在我们有了lambda之前是必要的。
不幸的是,std::bind
与lambdas同时进入标准,因此它立即几乎无关紧要。
在 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)...);
});
//.....
}
通过这种方式,您不会完全获得完美的转发。捕获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));
});
//.....
}
- 运行同一解决方案的另一个项目的项目
- Project Euler问题4的错误解决方案
- 计算每个节点的树高,帮助我解释这个代码解决方案
- C++:Application.cpp中抛出了未解析的外部符号(解决方案在问题的末尾,供未来的读者参考)
- visual c++,如何获取解决方案目录中的代码
- 有没有办法在远程设备上打开和编辑visual Studio 2017解决方案
- C++Matching Brackets 2解决方案不起作用
- 在 ubuntu3 上C++ goto 定义有什么解决方案吗16.04?
- C++11 中不同类型的对象的 std::array 的替代方案
- 在 leetcode 上提交解决方案时出现堆栈缓冲区溢出错误
- 别名模板的专业化 C++11 中没有开销的最佳替代方案
- 我的固定时间步长与增量时间和插值的解决方案是错误的吗?
- 无法在问题解决方案中执行输出逻辑
- 最大的回文产品 - 程序未运行,编写解决方案但无法理解问题
- 从预序遍历构造 bst 的 c++ 和 python 解决方案之间的区别
- 在一个解决方案中针对第三方静态库 (Creo) 的不同版本(版本)进行构建
- 如何巧妙地编写两个函数——一个用于检查是否存在解决方案,另一个用于获取所有解决方案
- 使用 Git 处理 C++ Visual Studio 2019 解决方案的外部依赖项源代码管理的最佳方法是什么?
- N-queen问题:无法弄清楚为什么我的解决方案不起作用
- std::bind in Modern C++ 的替代方案