2010-04-19 7 views
23

Probablemente esta sea una explicación muy simple, pero voy a dar la mayor cantidad de historia de respaldo posible en caso de que esté equivocado. Disculpas avanzadas por ser tan detallado. Estoy usando gcc4.5, y me doy cuenta de que el soporte de C++ 0x todavía es algo experimental, pero voy a actuar en el supuesto de que hay una razón no relacionada con errores para el comportamiento que estoy viendo.Comportamiento extraño cuando se construye recursivamente un tipo de retorno para funciones variadicas

Estoy experimentando con plantillas de funciones variadas. El objetivo final era construir una lista de cons de std::pair. No pretendía ser un tipo personalizado, solo una cadena de objetos de pares. La función que construye la lista debería ser de alguna manera recursiva, con el valor final de retorno dependiendo del resultado de las llamadas recursivas. Como un giro adicional, los parámetros sucesivos se suman 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 consistía en la suma de los dos parámetros establecidos y la llamada recursiva. En retrospectiva, esta fue obviamente una estrategia defectuosa, porque la función no se declara cuando trato de averiguar su tipo de devolución, por lo que no tiene más remedio que resolver la versión no recursiva.

Eso lo entiendo. Donde me confundí fue la segunda iteración. Decidí convertir esas funciones en miembros estáticos de una clase de plantilla. Las llamadas de función en sí no están parametrizadas, sino que toda la clase es. Mi suposición era que cuando la función recursiva intenta generar su tipo de retorno, instanciaría una nueva versión de la estructura con su propia función estática, y todo se resolvería por sí solo.

El resultado fue: "Error: ninguna función coincidente para la llamada a BuildStruct<double, double, char, char>::Go(const char&, const char&)"

El código erróneo:

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 a BuildStruct siempre deben ser los mismos tipos como los argumentos enviados a BuildStruct::Go, pero en el código de error Go le 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é intenta llamar a la función incorrecta en lugar de simplemente no encontrar una función? Parece que solo se mezclan tipos de cualquier manera, y simplemente no puedo dar una explicación de por qué. Si agrego parámetros adicionales a la llamada inicial, siempre profundiza hasta el último paso antes de fallar, por lo que presumiblemente la recursividad está al menos parcialmente funcionando. Esto está en contraste directo con el intento inicial, que siempre falló al encontrar una llamada de función de inmediato.

En última instancia, he superado el problema, con una solución bastante elegante que apenas se parece a ninguno de los dos primeros intentos. Entonces sé cómo hacer lo que quiero hacer. Estoy buscando una explicación del fracaso que vi.

Código completo a seguir, ya que estoy seguro de que mi descripción verbal fue insuficiente. Primero algunos repetitivos, 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 struct:

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:

solución
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&)' 

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>' 
+0

No puedo intentarlo ahora, pero podría ser que la deducción del tipo de devolución diferida siga fallando. En el segundo caso, debe poder escribir el tipo de devolución: 'typedef std :: pair :: result_type> result_type;' (y de manera adecuada para la primera especialización) y utilícelos: 'static result_type Go (...); ' – visitor

+0

Creo que lo intenté, pero no recuerdo el resultado. Le daré otra oportunidad cuando llegue a casa. Gracias. –

+31

Buen señor, eso es * mucho * para leer :) –

Respuesta

2

Al leer los comentarios, parece bastante claro que se trata de un error muy localizado en una versión particular de G ++, y esa es toda la respuesta que siempre habrá.

+0

Llegué a esa conclusión hace un mes, gracias por responder, así que puedo hacer que desaparezca :) –

Cuestiones relacionadas