Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter pack argument consumption

It is possible to get the first element of the parameter pack like this

template <typename... Elements>
struct type_list
{
};

template <typename TypeList>
struct type_list_first_element
{
};

template <typename FirstElement, typename... OtherElements>
struct type_list_first_element<type_list<FirstElement, OtherElements...>>
{
    typedef FirstElement type;
};

int main()
{
   typedef type_list<int, float, char> list;
   typedef type_list_first_element<list>::type element;
   return 0;
}

but not possible to similary get the last element like this

template <typename... Elements>
struct type_list
{
};

template <typename TypeList>
struct type_list_last_element
{
};

template <typename LastElement, typename... OtherElements>
struct type_list_last_element<type_list<OtherElements..., LastElement>>
{
    typedef LastElement type;
};

int main()
{
   typedef type_list<int, float, char> list;
   typedef type_list_last_element<list>::type element;
   return 0;
}

with gcc 4.7.1 complaining:

error: 'type' in 'struct type_list_last_element<type_list<int, float, char>>' does not name a type

What paragraps from the standard describe this behaviour?

It seems to me that template parameter packs are greedy in a sense that they consume all matching arguments, which in this case means that OtherElements consumes all three arguments (int, float and char) and then there is nothing left for LastElement so the compilation fails. Am i correct in the assumption?

EDIT:

To clarify: I am not asking how to extract the last element from the parameter pack, i know how to do that. What i actually want is to pick the pack apart from the back as opposed to the front, and as such recursing all the way to the back for each element would be ineffective. Apparentely reversing the sequence beforehand is the most sensible choice.

like image 428
yuri kilochek Avatar asked Sep 16 '12 10:09

yuri kilochek


2 Answers

The relevant clause is the bullet at the end of 14.5.5:8:

14.5.5 Class template partial specializations [temp.class.spec]

8 - Within the argument list of a class template partial specialization, the following restrictions apply: [...]

  • An argument shall not contain an unexpanded parameter pack. If an argument is a pack expansion (14.5.3), it shall be the last argument in the template argument list.
like image 137
ecatmur Avatar answered Oct 11 '22 11:10

ecatmur


Observation:

  1. Last element of <first,...> is the same as last element of <...> if only ... is not empty.
  2. Last element of one element list <elem> is elem

So you have to do it recursively, with tail template:

Recursion:

template <typename TypeList>
struct type_list_last_element;

template <typename FirstElement, typename... OtherElements>
struct type_list_last_element<type_list<FirstElement, OtherElements...>>
{
    typedef typename type_list_last_element<type_list<OtherElements...>>::type type;
};

Tail:

template <typename LastElement>
struct type_list_last_element<type_list<LastElement>>
{
    typedef LastElement type;
};

[UPDATE] And usage:

int main()
{
   typedef type_list<int, float, char> list;
   typedef type_list_last_element<list>::type last;
   return 0;
}

[END UPDATE]

See ideone

like image 34
PiotrNycz Avatar answered Oct 11 '22 11:10

PiotrNycz