3

I am sharing with you an issue that I got with a class using variadic function parameters. It is the class Thread shown in the following code. It is a wrapper of std::thread in order to use the function pattern.

I wanted to use polymorphism with this function in inheriting the class Thread into a new class, Functor, but gcc returns the errors bellow:

#include <thread>
#include <iostream>

using namespace std;

template<class... Args>
class Thread
{
public:
    virtual void operator()(Args...) = 0;

    void run(Args... args)
    {
    std::thread t(std::forward< Thread<Args...> >(*this), std::forward<Args>(args)...);
    t.join();
    }
};

template<class... Args>
class Functor : public Thread<Args...>
{
public:
    // generates the errors bellow
    virtual void operator()(Args... /*args*/)
    {
    }

    // doesnot work since the pure virtual function wants another prototype of function.
    // void operator()(int)
    // {
    // }
};

int main()
{
    int a = 12;
    Functor<int> f;
    f.run(ref(a));

    return 0;
}
from t-Thread-args2.cpp:1:
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/tuple: In instantiation of ‘struct std::_Head_base, false>’:
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/tuple:215:12:   required from ‘struct std::_Tuple_impl, int>’
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/tuple:507:11:   required from ‘class std::tuple, int>’
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/functional:1601:39:   required from ‘struct std::_Bind_simple(int)>’
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/thread:133:9:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = Thread; _Args = {int}]’
t-Thread-args2.cpp:14:83:   required from ‘void Thread::run(Args ...) [with Args = {int}]’
t-Thread-args2.cpp:42:17:   required from here
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/tuple:166:13: error: cannot declare field ‘std::_Head_base, false>::_M_head_impl’ to be of abstract type ‘Thread’
t-Thread-args2.cpp:7:7: note:   because the following virtual functions are pure within ‘Thread’:
t-Thread-args2.cpp:10:18: note:     void Thread::operator()(Args ...) [with Args = {int}]

I dont really understand the error since the pure virtual function was well defined in the deriveted class. However, in moving the function run() into the derivated class (Functor) it works.

Thanks in advance, Caner

canercandan
  • 41
  • 1
  • 4

3 Answers3

2

As per [thread.thread.constr]§3, the type of the first argument of the std::thread constructor is F&&, with the requirement that F is MoveConstructible. In your case, F is Thread, which is not MoveConstructible.

In other words, the std::thread needs to store the functor by value, and you're forwarding the functor as Thread, which is abstract.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Do you mean that I have to inherit my F class from MoveConstructible ? – canercandan Feb 21 '13 at 09:37
  • @canercandan `MoveConstructible` is a concept, not a class. It means "an instance of the type can be constructed using a move constructor." Your `Thread` is abstract, so it cannot have instances created at all. You should either make `run()` templated by the actual most-derived class type, or (preferably) use `std::ref` as Mike suggested in his answer. – Angew is no longer proud of SO Feb 21 '13 at 09:40
2

The problem is:

std::forward< Thread<Args...> >(*this)

which tries to copy the Thread sub-object. Luckily it's abstract, so you get a compile error rather than unexpected runtime behaviour.

You want a reference wrapper instead:

std::ref(*this)
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

I considered the multiple advice provided by the participants to this topic including the use of std::ref and would like to share with you the working code version solving the issues I got with the previous code.

#include <thread>
#include <iostream>

using namespace std;

template<class... Args>
class Thread
{
public:
    virtual void operator()(Args...) = 0;

    void run(Args... args)
    {
    std::thread t(std::ref(*this), args...);
    t.join();
    }
};

template<class... Args>
class Functor : public Thread<Args...>
{
public:
    void operator()(int)
    {
        while (1)
            {
            cout << "42 "; cout.flush();
            }
    }
};

int main()
{
    int a = 12;
    Functor<int> f;
    f.run(ref(a));

    return 0;
}

Thanks again.

canercandan
  • 41
  • 1
  • 4
  • Please try to flesh out your answer a bit more. Just posting a code block without any explanation or context is not good and might not be of any value to people looking for an answer to this question in the future. – Xaver Kapeller May 02 '14 at 18:22
  • @XaverKapeller just edited the answer with more explanations. – canercandan Oct 11 '14 at 15:34