3
votes

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

3
The usage of std::forward doesn't make sense. It makes sense for function template, but in your code run() is not a function template.Nawaz
More specifically, std::forward doesn't make sense here because its argument is always *this, i.e. always an lvalue.jogojapan
@jogojapan: The argument to std::forward is almost always lvalue, as it usually has a name.Nawaz
@Nawaz True, I should have said always an lvalue reference.jogojapan
Thanks for these explaination guys, I follow your advises.canercandan

3 Answers

2
votes

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.

2
votes

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)
1
votes

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.