Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC error with variadic templates: "Sorry, unimplemented: cannot expand 'Identifier...' into a fixed-length argument list"

While doing variadic template programming in C++11 on GCC, once in a while I get an error that says "Sorry, unimplemented: cannot expand 'Identifier...' into a fixed-length arugment list." If I remove the "..." in the code then I get a different error: "error: parameter packs not expanded with '...'".

So if I have the "..." in, GCC calls that an error, and if I take the "..." out, GCC calls that an error too.

The only way I have been able to deal with this is to completely rewrite the template metaprogram from scratch using a different approach, and (with luck) I eventually come up with code that doesn't cause the error. But I would really like to know what I was doing wrong. Despite Googling for it and despite much experimentation, I can't pin down what it is that I'm doing differently between variadic template code that does produce this error, and code that does not have the error.

The wording of the error message seems to imply that the code should work according the C++11 standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?

Here's some code that produces the error. Note: I don't need you to write a correct implementation for me, but rather just to point out what is about my code that is causing this specific error

// Used as a container for a set of types. template <typename... Types> struct TypePack {     // Given a TypePack<T1, T2, T3> and T=T4, returns TypePack<T1, T2, T3, T4>     template <typename T>     struct Add     {         typedef TypePack<Types..., T> type;     }; };  // Takes the set (First, Others...) and, while N > 0, adds (First) to TPack. // TPack is a TypePack containing between 0 and N-1 types. template <int N, typename TPack, typename First, typename... Others> struct TypePackFirstN {     // sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list     typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type; };  // The stop condition for TypePackFirstN:  when N is 0, return the TypePack that has been built up. template <typename TPack, typename... Others> struct TypePackFirstN<0, TPack, Others...> //sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list {     typedef TPack type; }; 

EDIT: I've noticed that while a partial template instantiation that looks like does incur the error:

template <typename... T> struct SomeStruct<1, 2, 3, T...> {}; 

Rewriting it as this does not produce an error:

template <typename... T> struct SomeStruct<1, 2, 3, TypePack<T...>> {}; 

It seems that you can declare parameters to partial specializations to be variadic; i.e. this line is OK:

template <typename... T> 

But you cannot actually use those parameter packs in the specialization, i.e. this part is not OK:

SomeStruct<1, 2, 3, T...> 

The fact that you can make it work if you wrap the pack in some other type, i.e. like this:

SomeStruct<1, 2, 3, TypePack<T...>> 

to me implies that the declaration of the variadic parameter to a partial template specialization was successful, and you just can't use it directly. Can anyone confirm this?

like image 465
Dennis Avatar asked Jan 01 '10 20:01

Dennis


1 Answers

There is a trick to get this to work with gcc. The feature isn't fully implemented yet, but you can structure the code to avoid the unimplemented sections. Manually expanding a variadic template into a parameter list won't work. But template specialization can do that for you.

template< char head, char ... rest > struct head_broken {    static const char value = head; };  template< char ... all > struct head_works; // make the compiler hapy  template< char head, char ... rest > struct head_works<head,rest...> // specialization {    static const char value = head; };  template<char ... all > struct do_head {    static const char head = head_works<all...>::value;    //Sorry, unimplemented: cannot expand 'all...' into a fixed-length arugment list    //static const char head = head_broken<all...>::value; };  int main {    std::cout << head_works<'a','b','c','d'>::value << std::endl;    std::cout << head_broken<'a','b','c','d'>::value << std::endl;    std::cout << do_head<'a','b','c','d'>::head << std::endl; } 

I tested this with gcc 4.4.1

like image 158
deft_code Avatar answered Sep 30 '22 12:09

deft_code