std::thread with movable, non-copyable argument

≡放荡痞女 提交于 2019-12-05 19:44:06

问题


The following program doesn't build in VS11 beta, gcc 4.5, or 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();
}

This is because the argument type is not copyable, but the implementation attempts to copy it.

As far as I can tell, this program is well formed and should work. The requirements for std::thread seem to imply that movable, non-copyable arguments should work here. Specifically it says that the callable object and each argument shall satisfy the MoveConstructible requirements, and that INVOKE(DECAY_COPY(std::forward<F>(f)),DECAY_COPY(std::forward<Args>(args))...) shall be a valid expression.

In this case I think expression works out to something like:

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)));

And I don't think this is supposed to involve a copy of p. gcc at least can compile this expression, though VS11 does not.

  1. Am I wrong about the requirements and the arguments must be copyable?
  2. Does the standard leave any leeway on this issue for implementations to copy arguments?
  3. Or are the implementation I tried non-conforming?

回答1:


From 30.3.1.2, paragraph 3 and 4 of N3337:

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

Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements. INVOKE (DECAY_-COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2) shall be a valid expression.

Effects: Constructs an object of type thread. The new thread of execution executes INVOKE (DECAY_-COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) with the calls to DECAY_COPY being evaluated in the constructing thread. Any return value from this invocation is ignored. [ Note: This implies that any exceptions not thrown from the invocation of the copy of f will be thrown in the constructing thread, not the new thread. —end note ] If the invocation of INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) terminates with an uncaught exception, std::terminate shall be called.

So yes, this should work. If it doesn't, then that's a bug in your implementation.

Do note that any parameter movement/copying will happen on the new thread. You're passing references to another thread, so you need to make sure that they still exist until that thread starts.




回答2:


As an alternative, and as the standard std::thread idiom, you can pass a reference wrapper:

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

This creates an object of type std::reference_wrapper<int>, which has value semantics and wraps a reference to an int (i.e. copying the wrapper aliases the reference).



来源:https://stackoverflow.com/questions/10001751/stdthread-with-movable-non-copyable-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!