recursion - tipos - plantilla variadic recursiva para imprimir el contenido de un paquete de parámetros
recursividad en programacion (4)
¿Cómo es posible crear una plantilla variadica recursiva para imprimir el contenido de un paquete de parámetros? Estoy intentando con esto, pero no compila:
template <typename First, typename ...Args>
std::string type_name () {
return std::string(typeid(First).name()) + " " + type_name<Args...>();
}
std::string type_name () {
return "";
}
¿Cómo voy a terminar la recursión?
Como alternativa a la especialización parcial no existente para funciones, puede usar la sobrecarga en una clase de tipificación:
#include <string>
#include <iostream>
#include <typeinfo>
template <unsigned int N> struct NumberToType { };
template <typename T>
std::string my_type_name(NumberToType<0> = NumberToType<0>())
{
return std::string(typeid(T).name());
}
template <typename T, typename ...Args>
std::string my_type_name(NumberToType<sizeof...(Args)> = NumberToType<sizeof...(Args)>())
{
return std::string(typeid(T).name()) + " " + my_type_name<Args...>(NumberToType<sizeof...(Args)-1>());
}
int main()
{
std::cout << my_type_name<int, double, char>() << std::endl;
}
Como alternativa, puede descomprimir el paquete de parámetros in situ como en el siguiente ejemplo:
#include<string>
#include<iostream>
#include<typeinfo>
template <typename T, typename ...Args>
std::string type_name () {
std::string str = typeid(T).name();
int arr[] = { 0, (str += std::string{" "} + typeid(Args).name(), 0)... };
(void)arr;
return str;
}
int main() {
auto str = type_name<int, double, char>();
std::cout << str << std::endl;
}
La recursividad no es necesaria para hacer eso.
Debe usar la especialización parcial para finalizar la recursión, pero como no puede especializar parcialmente las funciones gratuitas en C ++, necesita crear una clase de implementación con una función de miembro estática.
template <typename... Args>
struct Impl;
template <typename First, typename... Args>
struct Impl<First, Args...>
{
static std::string name()
{
return std::string(typeid(First).name()) + " " + Impl<Args...>::name();
}
};
template <>
struct Impl<>
{
static std::string name()
{
return "";
}
};
template <typename... Args>
std::string type_name()
{
return Impl<Args...>::name();
}
int main()
{
std::cout << type_name<int, bool, char, double>() << std::endl; // "i b c d"
return 0;
}
Esa primera declaración de Impl
es solo una solución para una falla en g ++ 4.6 (y más abajo). No será necesario una vez que implemente plantillas variadic correctamente.
En realidad, hay una manera muy elegante de finalizar la recursión:
template <typename Last>
std::string type_name () {
return std::string(typeid(Last).name());
}
template <typename First, typename Second, typename ...Rest>
std::string type_name () {
return std::string(typeid(First).name()) + " " + type_name<Second, Rest...>();
}
Inicialmente probé la template <typename Last>
y la template <typename First, typename ...Rest>
pero eso se consideró ambiguo (Rest puede ser cero elementos). Esta pregunta entonces me mostró la solución definitiva: Error de compilación en la función de plantilla variable recursiva
Tenga en cuenta que para evitar un poco de duplicación de código, también puede hacer:
template <typename Last>
std::string type_name () {
return std::string(typeid(Last).name());
}
template <typename First, typename Second, typename ...Rest>
std::string type_name () {
return type_name<First>() + " " + type_name<Second, Rest...>();
}