0

Once again I need your help making std::thread work with templated objects. This time my issue is in one of the arguments.

I try passing a reference to a (valid) instantiation of a virtual class as one of the arguments of my thread, and it just doesn't work. Note that passing a pointer instead of a reference solves everything. But still, why can't I give a reference (which I prefer, for many reasons)

Small code reproducing my error :

#include <cstdio>
#include <thread>
#include <vector>

template<typename T>
class functor { public: virtual T operator()(T) const = 0; };

template<typename T>
class square : public functor<T>
{
    public:
        T operator()(T f) const { return f*f; }
};

template<typename T>
void thread(const functor<T>& func, size_t imin, size_t imax) // line1
{
    for(size_t i=imin; i<imax; ++i)
    {
        T arg = T(i);
        std::cout << arg << "=>" << (func)(arg)) << std::endl; // line2
    }
}

template<typename T>
void threadlauncher(const functor<T>& func, size_t imin, size_t imax, size_t nbthread)
{
    size_t window = (imax-imin)/nbthread + ((imax-imin)%nbthread?1:0);
    std::vector<std::thread> threads;
    for (size_t idthread=0; idthread<nbthread; ++idthread)
    {
        size_t tmin = std::min(window*(idthread+0), imax);
        size_t tmax = std::min(window*(idthread+1), imax);
        threads.push_back(
            std::thread(&thread<T>, func, tmin, tmax) // line3
        );
    }
    for (std::thread& thread : threads)
        thread.join();
}
int main()
{
    square<float> func;
    threadlauncher<float>(func, 0, 10, 3);
    return 0;
}

The compiler (gcc 4.9.2) tells me that my object is invalid because operator() is virtual in threadlauncher

Note that changing code to pass thread's argument using pointers solves this

void thread(const functor<T>* func, size_t imin, size_t imax) // line1
std::cout << arg << "=>" << (*func)(arg)) << std::endl; // line2
std::thread(&thread<T>, &func, tmin, tmax) // line3

How can I make him understand that my reference is valid ?

Edit

I get an long/unreadeble error message from the compiler. I guess the important part is

src/functorthreads.cc:50:38:   required from here
/usr/include/c++/4.9.2/functional:1713:9: erreur: invalid abstract parameter type ‘functor<float>’
         __type;
         ^
src/functorthreads.cc:6:7: note:   because the following virtual functions are pure within ‘functor<float>’:
 class functor
       ^
src/functorthreads.cc:9:13: note:   T functor<T>::operator()(T) const [with T = float]
   virtual T operator()(T) const = 0;
Amxx
  • 3,020
  • 2
  • 24
  • 45
  • What is the _exact_ error the compiler gives you? Chances are you're trying to instantiate a copy of `functor` when you create the thread. – Captain Obvlious Dec 01 '14 at 15:37
  • I edit my post with what I believe are relevant parts of the (very long) compiler error – Amxx Dec 01 '14 at 15:42
  • 1
    Use `std::cref`. The thread argument's getting copied internally, which slices it and causes the pure virtual error. This is exactly the same problem as if you'd tried to use `std::vector const&>`. – Cameron Dec 01 '14 at 15:47

1 Answers1

1

std::thread takes copies of it's arguments. It doesn't matter that the argument to the function is a reference....the thread constructor will still copy the arguments since they are being passed to another thread. That's why pointers work, since they are being copied.

If you know that your object will remain in scope throughout the lifetime of the thread, you can use std::ref to wrap your argument:

std::thread(&thread<T>, std::ref(func), tmin, tmax)
Jay Miller
  • 2,184
  • 12
  • 11
  • Thx. Aditionnaly to this ref, my real application's thread does have vectors in it's argument. Wrapping everything in `std::cref` solved the issue an improved the performance (no copy needed). I'm wondering why does `std::thread` makes copy ? Is it to avoid data races ? – Amxx Dec 01 '14 at 17:15