【问题标题】:std::thread with movable, non-copyable argument带有可移动、不可复制参数的 std::thread
【发布时间】:2012-04-17 14:35:38
【问题描述】:

以下程序未在 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&lt;F&gt;(f)),DECAY_COPY(std::forward&lt;Args&gt;(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. 还是我尝试的实现不合格?

【问题讨论】:

  • 您似乎正在通过副本传递线程参数(根据匿名函数签名)。参数类型不应该是std::unique_ptr&lt;int&gt;&amp;&amp;const std::unique_ptr&lt;int&gt;&amp;吗?
  • @André :不存在通过副本传递的东西;通过 value 传递参数将根据调用者传递左值还是右值来复制或移动。
  • @ildjarn:对不起,我的意思是“按价值”,而不是“按副本”。我突然想到按值传递参数将选择移动构造函数(如果可用)。

标签: c++ multithreading c++11 rvalue-reference


【解决方案1】:

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

template &lt;class F, class ...Args&gt; explicit thread(F&amp;&amp; f, Args&amp;&amp;... args);

要求FArgs 中的每个 Ti 应满足 @987654326 @ 要求。 INVOKE (DECAY_-COPY ( std::forward&lt;F&gt;(f)), DECAY_COPY (std::forward&lt;Args&gt;(args))...) (20.8.2) 应该是一个有效的表达式。

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

所以是的,这应该可以。如果没有,那么这就是你的实现中的一个错误。

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

【讨论】:

  • 现在我也无法在 clang 中重现该错误,即使我之前使用的代码位于源存储库中,并且我的历史记录中有确切的命令行。我想我也应该重新检查一下 vs11。
  • 实际上,问题似乎是 libc++ 的旧版本与最新版本。
  • 此代码也适用于我的 Just::Thread library 与 g++ 4.5 & 4.6 和 MSVC 2010。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-03
    • 2015-04-02
    • 2013-07-14
    • 1970-01-01
    • 1970-01-01
    • 2020-01-07
    相关资源
    最近更新 更多