3

I want to use a reference to an abstract class (A) as parameter type in an function which is invoked using std::thread. It seems not possible, because the compiler tries for some reason to compile: std::tuple<A>, even that in my code only a reference-type of A is used as parameter (never as value-type).

#include <cstdlib>
#include <thread>

class A {
public:
  virtual void a() = 0;
};

class B : public A {
public:
  virtual void a() {
  }
};

class C {
public:
  C(A& aRef) {
    thread = std::thread(&C::doSomething, this, aRef);
  }
private:
  void doSomething(A& aRef) {

  }
  std::thread thread;
};

int main(int argc, char* argv[]) {
  B b;
  C c(b);
  return EXIT_SUCCESS;
}

Will output on Visual Studio 2017:

error C2259: 'A': cannot instantiate abstract class
tuple(278): note: see reference to class template instantiation 'std::tuple<A>' being compiled
tuple(278): note: see reference to class template instantiation 'std::tuple<C *,A>' being compiled
thread(49): note: see reference to class template instantiation 'std::tuple<void (__thiscall C::* )(A &),C *,A>' being compiled
main.cpp(18): note: see reference to function template instantiation 'std::thread::thread<void(__thiscall C::* )(A &),C*const ,A&,void>(_Fn &&,C *const &&,A &)' being compiled

Why std::thread tries to compile std::tuple<A>? If I call C::doSomething directly from the main-thread, the code compiles fine.

Is there anything I'm missing here?

Constantin
  • 8,721
  • 13
  • 75
  • 126
  • 2
    Have you tried to use [`std::ref()`](http://en.cppreference.com/w/cpp/utility/functional/ref) ? The argument might get copied otherwise. – moooeeeep Oct 04 '17 at 08:24
  • 2
    you need to wrap the reference, either with `std::reference_wrapper` or with a lambda – 463035818_is_not_an_ai Oct 04 '17 at 08:24
  • 2
    `C(A& aRef) : thread(&C::doSomething, this, std::ref(aRef)) {}`. But better yet: `C(A& aRef) : thread([this, aRef] { doSomething(aRef); }) {}` – rustyx Oct 04 '17 at 08:34

1 Answers1

2

You need to wrap the reference in a std::reference_wrapper when you pass it as an argument to your thread.

You can use std::ref() to do that.

Have at look at this example:

#include <thread>
#include <utility>
#include <iostream>

struct A {
    virtual void a() = 0;
};

struct B : public A {
    virtual void a() { std::cout << "Hello World\n"; }
};

class C {
    void doSomething(A& aRef) { aRef.a(); }
    std::thread thread;
public:
    C(A& aRef) : thread(&C::doSomething, this, std::ref(aRef)) {}
    // or alternatively using a lambda:
    //C(A& aRef) : thread( [&](){ doSomething(aRef); } ) {}
    void join() { thread.join(); }
};

int main(int argc, char* argv[]) {
    B b;
    C c(b);
    c.join();
}

Compiles and runs like this:

$ g++ ~/tt.cc -std=c++11 -pthread && ./a.out
Hello World

For reference:

moooeeeep
  • 31,622
  • 22
  • 98
  • 187