unique_ptr
unique_ptr

Reputation: 586

variadic template specialization issue

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

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Related Questions