Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to declare properly the template taking function type as a parameter (like a std::function)

I found out that its not trivial to have a neat syntax like in:

std::function<int(float, bool)>

If I declare the function as:

template <class RetType, class... Args>
class function {};

It would be an ordinary syntax to define function's templated types:

function<int,float,bool> f;

But it works with strange trick with partial template specialization

template <class> class function; // #1

template <class RV, class... Args>
class function<RV(Args...)> {} // #2

Why is that? Why I need to give the template an empty general form with empty type parameter (#1) or otherwise it just won't compile

like image 879
barney Avatar asked Aug 18 '16 11:08

barney


3 Answers

This is just the way the language was designed. Primary templates can't do complex decomposition of types like that; you need to use partial specialization.

If I understand correctly, you would like to just write the second version without having to supply the primary template. But think about how the template arguments map to the template parameters:

template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}

function<int(float,bool)>; //1
function<int, float, bool>; //2

Option 1 is what you want to write, but note that you pass a single function type to a template where the parameters are a two type parameters and a type parameter pack. In other words, writing this without a primary template means that your template arguments wouldn't necessarily match the template parameters. Option 2 matches the template parameters, but it doesn't match the specialization.

This makes even less sense if you have more than one specialization:

template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}

template <class T, class RV, class Arg1, class... Args>
class function<RV (T::*) (Arg1, Args...)> {}

You could maybe think up some rules to infer the primary template from the specializations, but that seems pretty awful to me.

like image 87
TartanLlama Avatar answered Oct 12 '22 05:10

TartanLlama


You have to keep in mind that int (float, bool) is a type. More precisely, it's the type "function which takes two parameters of type float and bool, and returns int."

Since it's a type, it's obvious the template must have one type parameter in the place where you want to use the syntax int (float, bool).

At the same time, having just the function type is unwieldy. Sure, you could do it easily. For example, if all you need to do is some kind of a forwarder:

template <class T>
struct CallForwarder
{
  std::function<T> forward(std::function<T> f)
  {
    std::cout << "Forwarding!\n";
    return f;
  }
};

However, as soon as you want access to the "components" of the function type, you need a way to introduce identifiers for them. The most natural way to do so is the partial specialisation for function types, just as you did in your question (and just as std::function does).

like image 36
Angew is no longer proud of SO Avatar answered Oct 12 '22 05:10

Angew is no longer proud of SO


You can actually do this:

template <typename T>
class X { };

X<int(char)> x;

And inside the definition of X you could create a std::function<T> as you would create a std::function<int(char)>. The problem here is that you cannot (at least easily) access the return type and the type of the parameters of the argument (int and char here).

Using the "trick", you can access these without problem - It "simply" makes your code cleaner.

like image 38
Holt Avatar answered Oct 12 '22 06:10

Holt