Reputation: 586
I wanted a generic way to process functions and eventually call them so I wrote the code below the problem is that the code is compiling but when I tested it I get errors of kind
wrong number of template arguments (1, should be 0) auto al= action::apply<5>::value;
so I know that I'm doing something wrong but I couldn't find out what it is.
template<typename T, typename enabled=void, typename ...P>
struct action{
template<P... args>
struct apply{ };
};
template<typename FN,typename ...P>
struct action<FN, typename std::enable_if< std::is_function<FN>::value >::type ,P...>{
template<P... args>
struct apply{
static const decltype( std::result_of<FN(P...)>::type ) value = FN( std::forward<P>(args)...);
};
};
int test(int x)
{
return x;
}
int main()
{
auto al= action<decltype(test), int>::apply<5>::value;
std::cout << "result :" << al <<std::endl;
return 0;
}
Upvotes: 1
Views: 197
Reputation: 275946
This is your primary template:
template<typename T, typename enabled=void, typename ...P>
struct action
Whenever you do action<blah>
, the arguments are interpreted via this primary template.
action<decltype(test), int>
so here, you pass decltype(test)
as T
, and int
as enabled
. And nothing as ...P
.
We then test to see if any of the template specializations apply.
template<typename FN,typename ...P>
struct action<FN, typename std::enable_if< std::is_function<FN>::value >::type ,P...>
This needs a break down. The part after action
is what we pattern match against. The part before is just some variables we deduce from it.
So
action<decltype(test), int>
struct action<FN, typename std::enable_if< std::is_function<FN>::value >::type ,P...>
line them up:
action<decltype(test), int>
struct action<FN , typename std::enable_if< std::is_function<FN>::value >::type ,P...>
and here are how they correspond:
FN=decltype(test)
typename std::enable_if< std::is_function<FN>::value >::type=int
P...=
Ok, so argument 1 is decltype(test)
, aka int(int)
. We deduce this to be FN
. All good.
Next, there are 2 arguments. So ...P
is clearly empty.
Argument 2 is in a non-deduced context. We'll calculated it:
typename std::enable_if< std::is_function<FN>::value >::type
typename std::enable_if< std::is_function<int(int)>::value >::type
typename std::enable_if< true >::type
void
...well, this specialization says it is void
. But we passed int
. As void=int
is nonsense, this specialization does not apply. We have failed to pattern match here.
Thus we go back to the primary specialization:
template<typename T, typename enabled=void, typename ...P>
struct action{
template<P... args>
struct apply{ };
};
ok, so it has a member template apply
that ... only takes 0 non-type arguments, because ...P
is empty.
Hence your error.
Specializations are not overloads. They are pattern matching implementation replacements of the primary template. The primary template arguments are the signature, always. The specialization can rename and pattern match certain choices.
You probably want to use an alias. Rename action
to action_helper
. Optionally put it in a details
namespace. Then:
template<class T, class...P>
using action = action_helper<T,void,P...>;
and use action
in client code.
Upvotes: 8