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
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.
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With