Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forwarding non-copyable type to std::thread

The following program does not build in GCC 4.9.2 or 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); 
}

This is because Foo is not copyable and forwarding an instance of Foo to an std:thread attempts to copy it (why?). However, if you forward the same instance to the lambda directly it compiles.

I guess, I don't fully understand all the requirements of the std::thread constructor. Any help would be greatly appreciated.

EDIT: Case 2 does not work even if we make Foo movable. Any clue?

Thanks.


1 Answers

http://en.cppreference.com/w/cpp/thread/thread/thread says: "First the constructor copies/moves all arguments args... to thread-accessible storage"

However, Foo is neither copyable nor movable. If you add the move constructor to Foo, then it compiles:

struct Foo {
  Foo() {};

  Foo(const Foo &) = delete;
  Foo &operator=(const Foo &) = delete;

  Foo(Foo &&) = default; // newly added
};

This does not work if you want to pass an lvalue to the thread: the thread constructor tries to copy or move the Foo object. However, Foo is not copyable, and you can't move from an lvalue (my_foo). You have these possibilities:

  • Turn the lvalue into an rvalue:

    my_function<Bar>(std::move(my_foo));

This is dangerous, because now my_foo is in an unusable state.

  • Wrap the lvalue with a reference and pass the reference wrapper to the thread:

    my_function<Bar>(std::cref(my_foo));

(or std::ref, if you want to modify my_foo)

like image 171
Bulletmagnet Avatar answered Apr 09 '26 15:04

Bulletmagnet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!