2010-01-01 15 views
36

Mentre faccio una programmazione di template variadic in C++ 11 su GCC, una volta ogni tanto ottengo un errore che dice "Spiacente, non implementato: impossibile espandere 'Identificatore ...' in un elenco di archi a lunghezza fissa." Se rimuovo "..." nel codice, viene visualizzato un errore diverso: "errore: pacchetti di parametri non espansi con '...'".Errore GCC con modelli variadici: "Spiacente, non implementato: impossibile espandere 'Identificatore ...' in un elenco di argomenti a lunghezza fissa"

Quindi, se ho la "..." in, GCC chiamate che un errore, e se prendo il "..." fuori, GCC chiamate che un errore troppo.

L'unico modo in cui sono riuscito a risolvere questo problema è di riscrivere completamente il metaprogramma del modello da zero utilizzando un approccio diverso e (con un po 'di fortuna) alla fine mi viene il codice che non causa l'errore. Ma mi piacerebbe davvero sapere cosa stavo facendo male. Nonostante Googling per questo e nonostante molti esperimenti, non riesco a definire cosa stia facendo in modo diverso tra il codice del modello variadic che produce questo errore e il codice che non ha l'errore.

La formulazione del messaggio di errore sembra implicare che il codice dovrebbe funzionare secondo lo standard C++ 11, ma GCC non lo supporta ancora. O forse è un bug del compilatore?

Ecco alcuni codice che genera l'errore. Nota: non si ha bisogno di scrivere una corretta implementazione per me, ma solo per sottolineare ciò che è di circa il mio codice che causa questo errore specifico

// 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: ho notato che, mentre un modello di un'istanza parziale che sembra non subire l'errore:

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

riscrittura come questo non produce un errore:

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

Sembra che si può dichiarare parametri per specializzazioni parziali per essere variadic; cioè questa linea è OK:

template <typename... T> 

Ma non si può in realtà uso quei pacchetti di parametri nella specializzazione, cioè questa parte non è OK:

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

Il fatto che si può farlo funzionare se si avvolge il pacchetto in un altro tipo, vale a dire in questo modo:

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

mi implica che la dichiarazione di del parametro variadic per una specializzazione del modello parziale ha avuto esito positivo e non è possibile utilizzarlo direttamente. Qualcuno può confermarlo?

+0

Può funzionare ora con 'clang 3.2' – alfC

risposta

48

C'è un trucco per farlo funzionare con gcc. La funzione non è ancora completamente implementata, ma è possibile strutturare il codice per evitare le sezioni non implementate. L'espansione manuale di un modello variadico in un elenco di parametri non funzionerà. Ma la specializzazione dei modelli può farlo per te.

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; 
} 

Ho provato questo con gcc 4.4.1

+0

Questa dovrebbe essere la risposta migliore. La specializzazione è la via da seguire con gcc 4.4. –

+0

Ho modificato questo concetto con la risposta accettata perché in realtà ho appena utilizzato il work-around di questa risposta e ha risolto il mio problema. Nota che se hai un sacco di codice template, usare questo lavoro sembra consumare più tempo/dimensioni di compilazione rispetto a no. Tuttavia, dopo aver scoperto che questo aumentava le dimensioni e il tempo di costruzione, ho scoperto che ero in grado di eseguire il rollup in un modello esterno che già avevo già specializzato, e quindi ho ammortizzato il costo utilizzando la specializzazione per il lavoro svolto. – Dennis

+0

Grazie, ha funzionato come un fascino. – terminus

2

Quale versione di GCC stai usando? Secondo questo GCC status page, GCC 4.4 dovrebbe supportarlo.

Test con GCC 4.4.2, ottengo errore simile.

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

Questo è corretto, GCC capisce il codice ma non può ancora sputare GIMPLE per questo.

Per quanto riguarda l'errore, è l'estensione dell'elenco di variabili del modello all'elenco di variabili di un altro modello.

+0

Sto usando GCC 4.4.1. – Dennis

+0

Puoi spiegare più dettagliatamente cosa significa estendere un elenco di variabili modello in un altro elenco di modelli? Stavo cominciando a pensare che avesse a che fare con il non poter usare direttamente i template template variadic come argomenti per una specializzazione del template. – Dennis

+2

Se l'espansione del pacchetto crea un elenco di argomenti del modello in cui alcuni argomenti raggiungono i parametri fissi e alcuni colpiscono un pacchetto di parametri del modello, allora sembra che si sia verificato questo errore. Ho riscritto il tuo codice per questo, e funziona: http://codepad.org/IBT60IbJ (ho dovuto introdurre un'altra specializzazione parziale, per fare in modo che il caso '<0, A, B>' scelga una specializzazione uniq). In questo caso, ogni espansione del pacchetto raggiunge sempre solo un corrispondente pacchetto di parametri. –

3

Per quanto ho capito l'errore viene segnalato perché il compilatore vede la dichiarazione della classe template come una classe con almeno 3 argomenti seguito dal optional quelli. Poiché si tenta di fare riferimento ad esso con 2 argomenti seguiti dall'elenco di espansione, questo viene confuso e genera questo errore. Al fine di rendere più compila correttamente basta dichiarare prima il modello come questo:

template <int N, typename TPack, typename... Others> 
struct TypePackFirstN; 

E dopo che la definizione di passo della ricorsione deve essere ricostruito come specializzazione modello. (questo è il trucco con gcc 4.5.0 20100404).

// 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<N, TPack, First, Others...> 
{ 
    // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone 
    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...>   // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone 
{ 
    typedef TPack type; 
}; 
1

La risposta di deft_code è corretta. Sto postando questo solo nel caso in cui sia utile vedere un confronto affiancato di codice rotto o fisso.

mi prendo il codice di esempio dal seguente domanda qualcuno ha postato che è stato duplicato a questo e ora è chiuso: Is ther a good workaround for GCC's "sorry, unimplemented: cannot expand ‘NEXT ...’ into a fixed-length argument list" error?

#include <iostream> 

template <int FIRST, int... NEXT> 
struct Test { 
    static const int VALUE = FIRST + Test<NEXT...>::VALUE; 
}; 

template <int FIRST> 
struct Test<FIRST> { 
    static const int VALUE = FIRST; 
}; 

int main() { 
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6" 
    return 0; 
} 

Questa, quando viene compilato, dà:

g++ -std=c++11 -o test test.cc 
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list 

Ma funziona (ho aggiunto commenti in cui il codice è stato modificato):

#include <iostream> 

template <int ... ALL> // Adeed 
struct Test;   // Added 

template <int FIRST, int... NEXT> 
struct Test<FIRST, NEXT...> { // Note: specialized with <FIRST, NEXT...> 
    static const int VALUE = FIRST + Test<NEXT...>::VALUE; 
}; 

template <int FIRST> 
struct Test<FIRST> { 
    static const int VALUE = FIRST; 
}; 

int main() { 
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6" 
    return 0; 
} 

Nota cosa è stato fatto in questa modifica di tre righe contrassegnata dai commenti: in origine era stato creato un modello specifico del modello variadico appena aggiunto.

Problemi correlati