c++ - tipos - sintaxis de recursividad
Comportamiento extraño cuando se construye recursivamente un tipo de retorno para funciones variad (1)
Al leer los comentarios, parece suficientemente claro que este es un error muy localizado en una versión particular de G ++, y esa es la única respuesta que habrá.
Probablemente, esta será una explicación muy simple, pero daré la mayor cantidad de antecedentes posibles en caso de que me equivoque. Disculpas avanzadas por ser tan verbosa. Estoy usando gcc4.5, y me doy cuenta de que el soporte de c ++ 0x todavía es un tanto experimental, pero voy a actuar en el supuesto de que hay una razón no relacionada con un error para el comportamiento que estoy viendo.
Estoy experimentando con plantillas de función variad. El objetivo final era construir una lista de contras de std::pair
. No estaba destinado a ser un tipo personalizado, solo una cadena de objetos de par. La función que construye la lista tendría que ser de algún modo recursiva, y el valor de retorno final dependerá del resultado de las llamadas recursivas. Como un giro adicional, los parámetros sucesivos se agregan juntos antes de insertarse en la lista. Entonces, si paso [1, 2, 3, 4, 5, 6], el resultado final debería ser {1 + 2, {3 + 4, 5 + 6}}.
Mi primer intento fue bastante ingenuo. Una función, Build, con dos sobrecargas. Uno tomó dos parámetros idénticos y simplemente devolvió su suma. El otro tomó dos parámetros y un paquete de parámetros. El valor de retorno era un par que consiste en la suma de los dos parámetros establecidos y la llamada recursiva. En retrospectiva, esta fue obviamente una estrategia defectuosa, ya que la función no se declara cuando trato de averiguar su tipo de retorno, por lo que no tiene más remedio que resolver la versión no recursiva.
Que entiendo Donde me confundí fue la segunda iteración. Decidí convertir esas funciones en miembros estáticos de una clase de plantilla. Las funciones llamadas a sí mismas no están parametrizadas, sino que toda la clase lo está. Mi suposición era que cuando la función recursiva intenta generar su tipo de retorno, se instancia una nueva versión de la estructura con su propia función estática, y todo se resolvería por sí mismo.
El resultado fue: "error: no hay función coincidente para la llamada a BuildStruct<double, double, char, char>::Go(const char&, const char&)
"
El código infractor:
static auto Go(const Type& t0, const Type& t1, const Types&... rest)
-> std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))>
Mi confusión proviene del hecho de que los parámetros de BuildStruct
siempre deben ser del mismo tipo que los argumentos enviados a BuildStruct::Go
, pero en el código de error faltan los dos parámetros dobles iniciales. ¿Que me estoy perdiendo aqui? Si mi suposición inicial sobre cómo se elegirían las funciones estáticas era incorrecta, ¿por qué está tratando de llamar a la función incorrecta en lugar de simplemente no encontrar una función en absoluto? Parece que se trata solo de mezclar tipos de cualquier manera, y no puedo dar una explicación de por qué. Si agrego parámetros adicionales a la llamada inicial, siempre se entierra al último paso antes de fallar, por lo que probablemente la recursión en sí está funcionando, al menos parcialmente. Esto está en contraste directo con el intento inicial, que siempre falló al encontrar una llamada a la función de inmediato.
En última instancia, he superado el problema, con una solución bastante elegante que casi no se parece a ninguno de los dos primeros intentos. Así que sé cómo hacer lo que quiero hacer. Estoy buscando una explicación para el fracaso que vi.
Código completo a seguir ya que estoy seguro de que mi descripción verbal era insuficiente. Primero un poco de repetición, si te sientes obligado a ejecutar el código y verlo por ti mismo. Luego el intento inicial, que falló razonablemente, luego el segundo intento, que no lo hizo.
#include <iostream>
using std::cout;
using std::endl;
#include <utility>
template<typename T1, typename T2>
std::ostream& operator <<(std::ostream& str, const std::pair<T1, T2>& p) {
return str << "[" << p.first << ", " << p.second << "]";
}
//Insert code here
int main() {
Execute(5, 6, 4.3, 2.2, ''c'', ''d'');
Execute(5, 6, 4.3, 2.2);
Execute(5, 6);
return 0;
}
Solución no estructurada:
template<typename Type>
Type BuildFunction(const Type& t0, const Type& t1) {
return t0 + t1;
}
template<typename Type, typename... Rest>
auto BuildFunction(const Type& t0, const Type& t1, const Rest&... rest)
-> std::pair<Type, decltype(BuildFunction(rest...))> {
return std::pair<Type, decltype(BuildFunction(rest...))>
(t0 + t1, BuildFunction(rest...));
}
template<typename... Types>
void Execute(const Types&... t) {
cout << BuildFunction(t...) << endl;
}
Errores resultantes:
test.cpp: In function ''void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]'':
test.cpp:33:35: instantiated from here
test.cpp:28:3: error: no matching function for call to ''BuildFunction(const int&, const int&, const double&, const double&, const char&, const char&)''
Solución de Struct:
template<typename... Types>
struct BuildStruct;
template<typename Type>
struct BuildStruct<Type, Type> {
static Type Go(const Type& t0, const Type& t1) { return t0 + t1; }
};
template<typename Type, typename... Types>
struct BuildStruct<Type, Type, Types...> {
static auto Go(const Type& t0, const Type& t1, const Types&... rest)
-> std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))> {
return std::pair<Type, decltype(BuildStruct<Types...>::Go(rest...))>
(t0 + t1, BuildStruct<Types...>::Go(rest...));
}
};
template<typename... Types>
void Execute(const Types&... t) {
cout << BuildStruct<Types...>::Go(t...) << endl;
}
Errores resultantes:
test.cpp: In instantiation of ''BuildStruct<int, int, double, double, char, char>'':
test.cpp:33:3: instantiated from ''void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]''
test.cpp:38:41: instantiated from here
test.cpp:24:15: error: no matching function for call to ''BuildStruct<double, double, char, char>::Go(const char&, const char&)''
test.cpp:24:15: note: candidate is: static std::pair<Type, decltype (BuildStruct<Types ...>::Go(BuildStruct<Type, Type, Types ...>::Go::rest ...))> BuildStruct<Type, Type, Types ...>::Go(const Type&, const Type&, const Types& ...) [with Type = double, Types = {char, char}, decltype (BuildStruct<Types ...>::Go(BuildStruct<Type, Type, Types ...>::Go::rest ...)) = char]
test.cpp: In function ''void Execute(const Types& ...) [with Types = {int, int, double, double, char, char}]'':
test.cpp:38:41: instantiated from here
test.cpp:33:3: error: ''Go'' is not a member of ''BuildStruct<int, int, double, double, char, char>''