完美地将可变参数模板转发到标准线程

Perfect Forwarding Variadic Template to Standard Thread

本文关键字:转发 标准 线程 参数 变参 完美      更新时间:2023-10-16

我正在尝试制作一种 std::thread 的形式,在线程中执行的代码周围放置一个包装器。不幸的是,由于我对右值和我试图传递的模板化类型的Function理解不足,我无法编译它。这是我的代码:

#include <vector>
#include <thread>
#include <utility>
void Simple2(int a, int b) {}
template <typename Function, typename... Args>
void Wrapper(Function&& f, Args&&... a) {
  f(std::forward<Args>(a)...);
}
class Pool {
 public:
  template <typename Function, typename... Args>
  void Binder(Function&& f, Args&&... a) {
    std::thread t(Wrapper<Function, Args...>,
                  std::forward<Function>(f), std::forward<Args>(a)...);
  }
};
int main() {
  Wrapper(Simple2, 3, 4);       // Works
  Pool pool;
  pool.Binder(Simple2, 3, 4);   // Doesn't compile
}

这里看起来很重要的 Clang3.0 输出是:

/usr/include/c++/4.6/functional:1286:9: error: non-const lvalue reference to type 'void (int, int)' cannot bind to a value of unrelated type 'void (*)(int, int)'

note: in instantiation of function template specialization 'std::thread::thread<void (void (&)(int, int), int &&, int &&), void (&)(int, int), int, int>' requested here

我认为这暗示了Wrapper<Function, Args...>与 std::thread f, a...给出的右值不匹配。

奇怪的是,如果我将std::forward<Function>(f)更改为 std::ref(f),这将在 GCC4.9 和更新的 Clang 中编译。

这是传递函数和传递函数指针之间的差异产生差异的罕见情况之一。 如果您这样做:

pool.Binder(&Simple2, 3, 4);  

它应该有效。 或者,您也可以将其参数衰减Binder函数指针:

class Pool {
 public:
  template <typename Function, typename... Args>
  void Binder(Function&& f, Args&&... a) {
    std::thread t(Wrapper<typename std::decay<Function>::type, Args...>,
                  std::forward<Function>(f), std::forward<Args>(a)...);
  }
};

在 C++14 中简化为:

class Pool {
 public:
  template <typename Function, typename... Args>
  void Binder(Function&& f, Args&&... a) {
    std::thread t(Wrapper<std::decay_t<Function>, Args...>,
                  std::forward<Function>(f), std::forward<Args>(a)...);
  }
};