10

以下程序未在 VS11 beta、gcc 4.5 或 clang 3.1 中构建

#include <thread>
#include <memory>

int main() {
    std::unique_ptr<int> p;
    std::thread th([](std::unique_ptr<int>) {

    },std::move(p));
    th.join();
}

这是因为参数类型不可复制,但实现会尝试复制它。

据我所知,这个程序结构良好,应该可以工作。std::thread 的要求似乎意味着可移动的、不可复制的参数应该在这里工作。具体来说,它表示可调用对象和每个参数都应满足 MoveConstructible 要求,并且INVOKE(DECAY_COPY(std::forward<F>(f)),DECAY_COPY(std::forward<Args>(args))...)应是有效的表达式。

在这种情况下,我认为表达式类似于:

template <class T> typename std::decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }

std::unique_ptr<int> p;
auto f = [](std::unique_ptr<int>) {};

decay_copy(f)(decay_copy(std::move(p)));

而且我认为这不应该涉及p. gcc 至少可以编译这个表达式,尽管 VS11 不能。

  1. 我对要求有误吗?参数必须是可复制的?
  2. 该标准是否在此问题上为实现复制参数留有余地?
  3. 还是我尝试的实现不合格?
4

2 回答 2

14

从 30.3.1.2,N3337 的第 3 和 4 段开始:

template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

要求F并且每Ti一项Args都应满足MoveConstructible要求。INVOKE (DECAY_-COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)(20.8.2) 应该是一个有效的表达式。

效果:构造一个线程类型的对象。新的执行线程与在构造线程中被评估INVOKE (DECAY_-COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)的调用一起执行。DECAY_COPY此调用的任何返回值都将被忽略。[注意:这意味着调用副本时未抛出的任何异常f都将在构造线程中抛出,而不是在新线程中抛出。—end note ] 如果调用INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...)以未捕获的异常终止,则应调用 std::terminate。

所以是的,这应该有效。如果没有,那么这是您的实现中的错误。

请注意,任何参数移动/复制都将在新线程上发生。您将引用传递给另一个线程,因此您需要确保它们仍然存在,直到该线程启动。

于 2012-04-03T21:47:10.803 回答
3

作为替代方案,并且作为标准std::thread习惯用法,您可以传递引用包装器:

int p;
std::thread([](int & x) { /* ... */ }, std::ref(p));

这将创建一个类型为 的对象std::reference_wrapper<int>,该对象具有值语义并包装对 an 的引用int(即复制包装器别名引用)。

于 2012-04-03T22:19:32.197 回答