以下程序不构建在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也不起作用。任何线索?
感谢。
答案 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
)