Mike Kinghan
Mike Kinghan

Reputation: 61202

Why can a variadic class template have at most one parameter pack?

There are moments when wish I could write a class template parameterized by a punctuated list of variadic template parameter packs, e.g.

template<typename ...lhs, int Punct, typename ...rhs>
struct tuple_pair
{
    std::tuple<lhs...> _lhs;
    std::tuple<rhs...> _rhs;
};

or for that matter:

template<int ...lhs, typename Punct, int ...rhs>
struct seq_pair
{
    std::integer_sequence<int,lhs...> _lhs;
    std::integer_sequence<int,rhs...> _rhs;
};

These may very well be moments when I am wishing for a grubby hack, but anyhow of course the Standard says I can't have it: § 14.1.11:

If a template-parameter of a primary class template or alias template is a template parameter pack, it shall be the last template-parameter.

I do not understand why this is so. It seems to me that in any instantiation, e.g.

tuple_pair<char,short,0,int,long> tp;
seq_pair<0,2,3,void,4,5,6> sp;

a compiler could distinguish the ...lhs arguments from the ...rhs as well as I can.

I invite no speculations as to why the Standard is what is - emphatically so - but can anyone authoritatively tell us why the C++ template machinery does not or cannot support the separation of multiple class template parameter packs in this way? I would particularly like to have confirmed or dismissed the suspicion that there is a fundamental logical obstacle to it that escapes me.

Upvotes: 0

Views: 1441

Answers (1)

maninalift
maninalift

Reputation: 723

Variadic template lists cannot be manipulated as first class objects. As a result it is usually more convenient to work with parameter packs wrapped in some template object.

This is how I would go about passing two lists of types to a template class:

// create an empty default implementation
template <typename LeftTuple, typename RightTuple> 
class tuplePair {};

// specialise to allow tupled lists of types to be passed in
template <typename ...LHS, typename ...RHS>
class tuplePair<tuple<LHS...>, tuple<RHS...> > 
{
   // ...
};

//or more catholically:
template <typename ...LHS, typename ...RHS, template<typename...> class tuple_template>
class tuplePair<tuple_template<LHS...>, tuple_template<RHS...>>  
{
   // ...
};

template<typename... X>
class some_other_tuple {};



int main() {
   tuplePair<tuple<char,char,char>, tuple<char,char,char>> tango_tuple;
   tuplePair<some_other_tuple<int>, some_other_tuple<char>> other_tuple;
   return 0;
}

I this formulation clearer than using some sort of deliminator (void) in any case. As a general rule semantics that afford for a list or tuple object rather than simply using deliminators are more powerful (because they allow nesting) and easier to manipulate.

Addendum:

I will risk of giving another answer which misses the point, in particular it is not authoritative, in the hope that it might be helpful.

I have read through the drafts of the variadic template proposal and scanned all 196 threads on comp.std.C++ mentioning the word "variadic" and it seems that the only reason for this limitation is simplicity. In particular simplicity of drafting the standard rather than implementation.

I couldn't find any discussion of generalization that you present (allowing a parameter pack anywhere in the template parameter list which is not followed by a parameter or parameter pack of the same kind). However other generalizations were discussed, such as allowing a parameter pack expansion to appear in places other than the end of of a template specialization and it looks like time just ran out on these discussions.

Do you have a persuasive use case? I really don't mean to be the "show me a use case or I will shut you down" bully, I am just interested.

Upvotes: 4

Related Questions