Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial template specialization with multiple template parameter packs

Continuing my journey into the world of variadic templates, I encountered another problem.

Assuming the following template class:

template < typename T >
struct foo 
{
    //default implementation
};

it is possible to partially specialize it for variadic template instantiations like this:

template < template < typename ... > class T, typename ...Args >
struct foo< T< Args... > >
{
    //specialized implementation
};

With this, foo< int > will correspond to the default implementation and foo< std::tuple< int, char > > to the specialized implementation.

However, things become more complicated when using several template parameters. For example, if we have the following template class

template < typename T, typename U >
struct bar {};

and we want to partially specialize it as we did for foo, we cannot do

template < template < typename ... > class T, typename ...TArgs,
           template < typename ... > class U, typename ...UArgs >
struct bar< T< TArgs... >, U< UArgs... > > {};

//This would correspond to the specialized version with
//T=std::tuple,
//TArgs=int,char
//U=std::tuple,
//UArgs=float
bar< std::tuple< int, char >, std::tuple< float > > b;

Indeed, if I am correct, we can only have one template parameter pack and it must be positioned at the end of the parameter list. I understand why this is mandatory in template declarations, but for certain partial template specialization (like the example above), this should not be an issue.

Is it possible to achieve partial template specialization with multiple template parameter packs?


Edit: Now I feel silly...the code I gave above compiles perfectly (at least with gcc 4.5). The compile error I had was not because of multiple parameter packs, but because of their use as member functions parameters. In the partial specialization of bar, I tried to define a member function that takes both TArgs and UArgs parameters:

template < template < typename ... > class T, typename ...TArgs, 
           template < typename ... > class U, typename ...UArgs >
struct bar< T< TArgs... >, U< UArgs... > >
{
    void method( TArgs... targs, UArgs... uargs ) //compile error here
    {
    }
};

On the member function declaration, gcc gives me the error

parameters packs must be at the end of the parameter list.

As far as I can tell, the compiler should be able to define the correct member function for a given template instantiation, e.g. bar< std::tuple< int, char >, std::tuple< float > > should contain a member function void method( int, char, float ). Am I doing something wrong? Or am I trying to do something that is not possible? If so, is there a good reason why this is not possible?

like image 270
Luc Touraille Avatar asked Jan 16 '11 16:01

Luc Touraille


People also ask

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What is mean by template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

Why do we use template template parameter?

8. Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.

What can the template parameter in C++ template definition be?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


1 Answers

Probably this answer won't clear your question directly, but the following code compiled on ideone(gcc-4.5.1) when I tested.

#include <cstdio>
#include <tuple>

template< class, class > struct S {
  S() { puts("primary"); }
};

template<
  template< class... > class T, class...TArgs
, template< class... > class U, class...UArgs
>
struct S< T< TArgs... >, U< UArgs... > > {
  S() { puts("specialized"); }
};

int main()
{
  S< int, int >  p;                                       // "primary"
  S< std::tuple< int, char >, std::tuple< float > >  s;   // "specialised"
}

I'm not sure this code is strictly conformant, but as far as I read N3225 14.5.3, I couldn't find the statement which mentions that template parameter pack has to be the last template parameter.

Edit:
I reread N3225 and found the following statements:

8.3.5/4 If the parameter-declaration-clause terminates with an ellipsis or a function parameter pack (14.5.3), the number of arguments shall be equal to or greater than the number of parameters that do not have a default argument and are not function parameter packs.

14.8.2.5/10 [Note: A function parameter pack can only occur at the end of a parameter-declarationlist(8.3.5). -end note]

So, as you mentioned, function parameter pack has to be the last parameter unfortunately.
A non-template member function of a class template is an ordinary function for that class when it is instantiated(fully specialized). So I wish that the code in this question can be compiled logically, as a special case.

like image 123
Ise Wisteria Avatar answered Nov 16 '22 03:11

Ise Wisteria