3

I'm following this great tutorial. The author heavily uses variadic templates and I came to a point where I'm stuck, can't understand. Can you help me?

1. Why isn't this compiling?

// this is simple
template<size_t I, typename T>
struct tuple_element
{
    T value;
};

// this does NOT compiles: error: parameter pack 'Indices' must be at the end of the template parameter list
template <size_t... Indices, typename... Types>
struct tuple_impl : tuple_element<Indices, Types>...
{
};

Next, the author have this code that compiles fine:

template <size_t... Indices>
struct index_sequence
{
    using type = index_sequence<Indices...>;
};

template <typename Sequence, typename... Types>
struct tuple_impl;

template <size_t... Indices, typename... Types>
struct tuple_impl<index_sequence<Indices...>, Types...>
  : tuple_element<Indices, Types>...
{
};

2. Why in this case everything ok? I see almost the same pattern here: tuple_element<Indices, Types>...

3. Why this can't be compiled:

template <size_t... Indices>
void g(Indices...){}; //error: variable or field 'g' declared void 
2
  • Ask one question per question. I answered the first question only. Commented Jan 22, 2015 at 15:03
  • About #3: You are passing values (of type size_t) to the declared g(). In C++ rules this forms a "constructor call". Whatever stands before g is the type of the variable - that's why you have this message. Commented Jan 22, 2015 at 15:09

3 Answers 3

2

The error you are seeing is in the passing of parameters. This is a compile error:

template <size_t... Indices, typename... Types>
struct tuple_impl
{};

live example

The rule is you cannot have one pack followed by another in a template class template parameter list.

The second example is a specialization, where the pack rule does not exist. The template parameters of a specialization are merely the types and values which are extracted from the pattern matching against the primary templates, as guided by the <> portion of the specialization after the type name.

As they are never passed in in that same list & order, having one ... after another doesn't cause any ambiguity. With a primary template, the order matters, and anything after a ... is difficult to distinguish from more of the .... Probably to keep the compiler's job easier, even in cases where it cannot be ambiguous (say, a pack of literals followed by a pack of types), C++ bans its use.

Sign up to request clarification or add additional context in comments.

1 Comment

I've gotten around this in the past by a nested template, i.e. template <size_t... Indicies> struct Foo { template <typename... Types> class Bar;};, instantiated like Foo<1,2,3,4>::Bar<int,double,char,whatever>{}. The author did it with a dummy pack, which I like less because when someone (inevetably) does Bar<char,int> you'll get a "Undefined variable" with a bunch of unreadable template crap.
1
  1. The compiler would have no means to distinguish when the first sequence ends and the second starts - therefore it is allowed to have only one parameter pack in variadic templates, at the end.

    tuple_impl<a, b, c, d, e, f> //is d still an index or already a type? what about e?
    
  2. This works, because tuple_impl itself is a template that has itself only one parameter pack, the Types.... It just happes that in this specialization the first parameter is a template, too, which has a parameter pack, too. So, in contrast to one template with two packs, you have two templates with one pack each, which is ok.

  3. This does not have to do with variadic templates, i.e. it would not work with a single argument either, for the same reason. The fact is, that since Indices... are not types but values, you are not defining a function. If it was not void, the compiler would have had problems later. Consider this example, which is slightly modified but essentially a similar construct:

    template <size_t I>
    unsigned g(I)
    {}
    

The middle line is central: The compiler thinks that it is a variable definition, initialized with I. Therefore the error in your case, because variables of type void simply don't make sense. My compiler then emits a warning about the template, it thinks that g is a templated variable, and those are a C++1y extension. After that is done, he realizes the variable definition is not finished by a ;, emits an error and exits...

Comments

1

Just to add some important thing: this is a test snippet:

int main( int argc, char** argv )
{
    using i3 = index_sequence<1, 2, 3>;
    tuple_impl<i3, int, double, char> tup;

    return 0;
}

Note: here you pass this i3 as the "index pack". The "master template" always defines how the parameters have to be passed to the template. The template<...> statement if set for specialization does not define anything, but just what internally the parameter combination may spread inside the specialization, but it's not a part of public interface.

For example, if you try to use <1, 2, 3, int, double, char> as a parameter specification (which would theoretically match the template parameter specification for the specialization), it will fail to compile.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.