6

I've been trying more about multi threaded programming in c++, and i was having difficulty understanding std::promise so i began searching for answers on this website, and low and behold, there is somebody with the same question as me. But reading the answer made me even more confused this is the code in the answer that presumably is a similar implementation of std::packaged_task

template <typename> class my_task;

template <typename R, typename ...Args>
class my_task<R(Args...)>
{
    std::function<R(Args...)> fn;
    std::promise<R> pr;             // the promise of the result
public:
    template <typename ...Ts>
    explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }

    template <typename ...Ts>
    void operator()(Ts &&... ts)
    {
        pr.set_value(fn(std::forward<Ts>(ts)...));  // fulfill the promise
    }

    std::future<R> get_future() { return pr.get_future(); }

    // disable copy, default move
};

in this code,

1- what does this syntax mean template <typename R, typename ...Args> class my_task<R(Args...)>, more specifically, what is the purpose of <R(Args...)> ?

2- why is there a foroward decleration for the class?

thanks

GamefanA
  • 1,555
  • 2
  • 16
  • 23

2 Answers2

5

There was some brief discussion in the comments how 1 and 2 should be two separate questions, but I believe that they both are just two sides to the same exact question, for the following reasons:

template <typename> class my_task;

template <typename R, typename ...Args>
class my_task<R(Args...)>; ....

The first statement declares a template that takes a typename as its sole template parameter. The second statement declares a specialization for that template class.

In this context:

 R(Args...)

Will specialize for any typename that matches a function. This template specialization will match any template instantiation that passes a function signature for a typename. Barring any problems within the template itself, this template specialization will be used for:

 my_task<int (const char *)>

or, a function that takes a const char * parameter and returns an int. The template specialization will also match:

 my_task<Tptr *(Tptr **, int)>

or, a function that takes two parameters, Tptr ** and an int, and returns a Tptr * (here, Tptr is some other class).

The template specialization will NOT match:

 my_task<int>

Or

 my_task<char *>

Because they are not function signatures. If you try to instantiate this template using a non-function typename you're going to get a compilation error. Why?

Well, that's because the template is not defined:

template<typename> class my_task;

Don't think of this as just a forward declaration. it's a forward declaration of a template that takes a template parameter, and the template will not be defined anywhere. Rather, the template declaration allows for a subsequent template specialization declaration, that will match only specific types passed as a template parameter.

This is a common programming technique for restricting the kinds of typenames or classes that can be used with a particular template. Instead of allowing a template to be used with just any typename or class, the template can only be used with some subset. In this case, a function typename, or signature.

It also makes it easier for the template itself to explicitly reference -- in this case -- to the template parameter's return type, and the parameter types. If the template has just a bland, single typename as a template parameter, it can't easily access the function's return type, or the function parameter's types.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Thank you so much, now it all makes sense. I knew it was a specialization syntax, but i have never seen one like that before. – GamefanA Dec 02 '15 at 02:29
2

1: What does this syntax mean template <typename R, typename ...Args> class my_task<R(Args...)>

This is a specialization of the class template my_task. The <R(Args...)> after the name means it is specialized for that type, and that type is a function. R(Args...) is the type of a function taking Args parameters and returning R. So, my_task<void()> mt; for example would make Args be an empty parameter pack, and R would be void.

2: Why is there a forward declaration for the class?

The class is declared, but unlike an ordinary forward declaration, the un-specialized version isn't defined. This class is only intended to work when the type is a function, so if someone tries to use something that isn't a function (like my_task<int>), it will give an error about the type being undefined.

my_task<void*(int, int)> mt1; //R = void*, Args = int, int
my_task<int> mt2; //error: use of undefined class
Weak to Enuma Elish
  • 4,622
  • 3
  • 24
  • 36