将非可复制类型转发到std :: thread

时间:2015-02-03 01:57:34

标签: c++ multithreading c++11 move-semantics perfect-forwarding

以下程序不构建在GCC 4.9.2或clang 3.6中:

#include <iostream>
#include <vector>
#include <thread>

/* Non-copyable type */
struct Foo {
  Foo() {};

  Foo(const Foo &) = delete;
  Foo &operator=(const Foo &) = delete;

  Foo(Foo &&) = default; //EDIT: This fixes CASE 1
};

/* Bar depends on Foo */
struct Bar {
  Bar(const Foo &) {}
};

template <class T, class... Args> 
void my_function(Args &&... args) {
  std::vector<T> data;
  auto lambda = [&data](Args &&... ts) { data.emplace_back(ts...); };

  lambda(std::forward<Args>(args)...);              // <-- Compile
  std::thread(lambda, std::forward<Args>(args)...); //<--- Does NOT compile
}

int main() { 
  //CASE 1: (works)
  // my_function<Bar>(Foo());
  //CASE 2: (does not work)
  Foo my_foo;
  my_function<Bar>(my_foo); 
}

这是因为Foo不可复制并且将Foo的实例转发到std:thread尝试复制它(为什么?)。但是,如果您将同一个实例直接转发给lambda,则会进行编译。

我想,我不完全理解std :: thread构造函数的所有要求。任何帮助将不胜感激。

编辑:即使我们让Foo移动,案例2也不起作用。任何线索?

感谢。

1 个答案:

答案 0 :(得分:2)

http://en.cppreference.com/w/cpp/thread/thread/thread说:&#34;首先,构造函数将所有参数args复制/移动到线程可访问的存储中&#34;

然而,Foo既不是可复制的也不是可移动的。如果将移动构造函数添加到Foo,则编译:

struct Foo {
  Foo() {};

  Foo(const Foo &) = delete;
  Foo &operator=(const Foo &) = delete;

  Foo(Foo &&) = default; // newly added
};

如果要将左值传递给线程,则不起作用:线程构造函数尝试复制或移动Foo对象。但是,Foo不可复制,您无法从左值(my_foo)移动。你有这些可能性:

  • 将左值变为右值:

    my_function<Bar>(std::move(my_foo));

这很危险,因为现在my_foo处于无法使用的状态。

  • 用引用包装左值并将引用包装器传递给线程:

    my_function<Bar>(std::cref(my_foo));

(或std::ref,如果您想修改my_foo