c++ - Error de GCC con plantillas variadic: "Disculpe, no implementado: no puede expandir ''Identificador...'' en una lista de argumentos de longitud fija"
templates g++ (4)
Al hacer la programación de plantilla variadica en C ++ 11 en GCC, de vez en cuando recibo un error que dice "Lo siento, no implementado: no se puede expandir ''Identificador ...'' en una lista de algoritmos de longitud fija." Si elimino el "..." en el código, aparece un error diferente: "error: paquetes de parámetros no expandidos con ''...''".
Entonces, si tengo el "..." en, GCC llama a eso un error, y si elimino el "...", GCC también llama a eso un error.
La única forma en que he podido lidiar con esto es reescribiendo por completo el metaprograma de plantillas desde cero utilizando un enfoque diferente, y (con suerte) finalmente me aparece un código que no causa el error. Pero realmente me gustaría saber qué estaba haciendo mal. A pesar de buscar en Google y, a pesar de mucha experimentación, no puedo precisar qué es lo que estoy haciendo de manera diferente entre el código de plantilla variadic que produce este error y el código que no tiene el error.
La redacción del mensaje de error parece implicar que el código debería funcionar de acuerdo con el estándar C ++ 11, pero que GCC aún no lo admite. O tal vez es un error del compilador?
Aquí hay un código que produce el error. Nota: No necesito que me escriba una implementación correcta, sino solo señalar qué hay sobre mi código que está causando este error específico
// 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;
};
EDITAR: Me he dado cuenta de que, si bien una creación de instancias de plantilla parcial parece tener el error:
template <typename... T>
struct SomeStruct<1, 2, 3, T...> {};
Reescribirlo ya que esto no produce un error:
template <typename... T>
struct SomeStruct<1, 2, 3, TypePack<T...>> {};
Parece que puede declarar parámetros a especializaciones parciales para que sean variados; es decir, esta línea está bien:
template <typename... T>
Pero no puedes usar esos paquetes de parámetros en la especialización, es decir, esta parte no está bien:
SomeStruct<1, 2, 3, T...>
El hecho de que puedes hacer que funcione si envuelves el paquete en algún otro tipo, es decir, así:
SomeStruct<1, 2, 3, TypePack<T...>>
para mí implica que la declaración del parámetro variadic a una especialización de plantilla parcial fue exitosa, y simplemente no se puede usar directamente. ¿Alguien puede confirmar esto?
¿Qué versión de GCC estás usando? De acuerdo con esta página de estado de GCC , GCC 4.4 debería ser compatible.
Probando con GCC 4.4.2, obtengo un error similar.
La redacción del mensaje de error parece implicar que el código debería funcionar de acuerdo con el estándar C ++ 0x, pero que GCC aún no lo admite. O tal vez es un error del compilador?
Esto es correcto, GCC entiende el código pero aún no puede escupir a GIMPLE por él.
En cuanto a qué causa el error, es la extensión de la lista de variables de la plantilla a la lista de variables de otra plantilla.
Hay un truco para hacer que esto funcione con gcc. La función aún no está completamente implementada, pero puede estructurar el código para evitar las secciones no implementadas. La expansión manual de una plantilla variable en una lista de parámetros no funcionará. Pero la especialización de plantilla puede hacer eso por usted.
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;
}
Probé esto con gcc 4.4.1
La respuesta de deft_code es correcta. Estoy publicando esto en caso de que sea útil ver una comparación lado a lado del código roto versus el código fijo.
Tomaré el ejemplo del código de la siguiente pregunta que alguien publicó que se duplicó a este y ahora está cerrado: ¿Hay una buena solución para "lo siento, no implementado" de GCC: no se puede expandir ''SIGUIENTE ...'' en un argumento de longitud fija lista "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;
}
Esto, cuando se compila, da:
g++ -std=c++11 -o test test.cc
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list
Pero esto funciona (agregué comentarios donde se cambió el código):
#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;
}
Tenga en cuenta lo que se hizo en este cambio de tres líneas marcado por los comentarios: lo que originalmente era la primera plantilla se hizo una plantilla especializada de la plantilla variadic recién agregada.
Por lo que yo entiendo, el error se informa porque el compilador ve la declaración de la clase de la plantilla como una clase con al menos 3 argumentos seguidos de los opcionales. Como intenta referirse a él con 2 argumentos seguidos por la lista de expansión, se confunde y emite este error. Para hacer que se compile correctamente, primero debes declarar la plantilla así:
template <int N, typename TPack, typename... Others>
struct TypePackFirstN;
Y después de eso, la definición del paso de recursión debe ser actualizada como una especialización de plantilla. (Esto funciona 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;
};