10

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?
bames53
  • 86,085
  • 15
  • 179
  • 244
  • You seem to be passing the thread argument by copy (as per the anonymous function signature). Shouldn't the argument type be `std::unique_ptr&&` or `const std::unique_ptr&`? – André Caron Apr 03 '12 at 21:36
  • 3
    @André : There is no such thing as passing by copy; passing the argument by _value_ will copy or move depending on whether the caller passes an lvalue or an rvalue. – ildjarn Apr 03 '12 at 21:47
  • 1
    @ildjarn: sorry, I meant "by value", not "by copy". It slipped my mind that passing arguments by value will select the move constructor if one is available. – André Caron Apr 03 '12 at 22:23

2 Answers2

14

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.

Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • And now I can no longer reproduce the error in clang either, even though the code I used earlier is in a source repository and I have the exact command line in my history. I guess I should recheck vs11 too. – bames53 Apr 04 '12 at 00:55
  • 1
    Actually it looks like the issue was an old version of libc++ vs the latest. – bames53 Apr 04 '12 at 00:56
  • 2
    This code also works with my [Just::Thread library](http://www.stdthread.co.uk) with g++ 4.5 & 4.6 and MSVC 2010. – Anthony Williams Apr 04 '12 at 07:29
3

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

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084