c++ - 现代 C++ 中 std::bind 的替代方案

标签 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...));

    //.....
}

还有一些问题我还没有解决。

  1. 有没有办法删除 std::bind 并存储不同的对象或指针?
  2. 这可以更通用吗?我能否以某种方式将返回不同对象的函数存储在单个容器中(int,void...)?
  3. 创建任务的一些逻辑可以在编译时执行吗?比如consexpr bind。

最佳答案

来自 OP 的评论。

There are. This is simplified. I'm using futures and a special container in the real code. It is meant to be used in a multithreading environment

这叫做埋葬lede。

如果您正在存储要在其他线程中调用的可调用对象,则在其他线程中您需要签名 void() .在这个线程中你想要一个std::future被填充。

至于绑定(bind)参数,同时有多个 std函数为你做这件事,我发现最好要求带有预绑定(bind)参数的可调用对象。他们可以在外面做,使用std::bind或 lambda 或他们选择的任何其他方式。

所以这就来了

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它可以产生,这是一种浪费,但它比编写自己的仅移动调用一次拥有函数对象要短。

我个人刚刚给自己写了一个轻量级的 move-only std::function<void()> esque 类而不是使用 std::packaged_task<void()> ,但这可能是不明智的。

addTask 返回的 future 当 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
};

在 C++17 中,您的绑定(bind)的替代品如下所示:

[
  func = std::forward<Func>(func),
  args = std::make_tuple( std::forward<Args>(args)... )
]() mutable {
  std::apply( func, std::move(args) );
}

在 C++14 中你可以写 notstd::apply相当容易。 Move-into-lambda 需要 C++14,因此如果您需要有效地移动参数,您需要 std bind 或 C++11 中的手动函数对象。

我会争辩说,将参数强绑定(bind)放在代码域中使用线程池是最好的。

这也允许线程池做一些事情,比如传递任务可选的额外参数,比如“取消标记”等等。

关于c++ - 现代 C++ 中 std::bind 的替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46118564/

相关文章:

c++ - 使用 std::variant 作为类成员并应用访问者

c++ - 检查所有可变参数模板参数的特征

c++ - 英特尔 TBB 中的任务延续

c++ - 通过翻转单元格组用零填充二维数组

c++ - 对于确实存在并被使用的制服,OpenGL 返回 -1

c++ - gcc 的 STL 现在支持右值引用吗?

c++ - 是否可以使用模板类的单个参数将类型和该类型的指针传递给 C++ 模板类?

c++ - 如何将 std::bind 作为通用引用类型传递?

c++ - GCC 无法使用 init-capture 捕获 'this' 指向模板类型的指针

C++ 编译器正在更改我的结构的对齐方式。我怎样才能防止这种情况发生?