c++ - Devuelve diferentes tipos con un valor de parámetro de plantilla diferente(pero el mismo tipo)
templates template-specialization (3)
Declaró la misma plantilla 3 veces, mientras que en realidad desea especializaciones. Hasta donde sé, no puede especializarse en el tipo de retorno directamente (*). Sin embargo, no hay nada que no pueda resolverse con una capa adicional de indirección. Puedes hacer algo en la línea de:
#include <string>
template <int> struct return_type_tag {};
template <> struct return_type_tag<0> { using type = int; };
template <> struct return_type_tag<1> { using type = float; };
template <> struct return_type_tag<2> { using type = std::string; };
template <int x> typename return_type_tag<x>::type test();
template<> int test<0>() { return 8; }
template<> float test<1>() { return 8.8; }
template<> std::string test<2>() { return "8.9"; }
int main()
{
int a = test<0>();
float b = test<1>();
std::string c = test<2>();
return 0;
}
(*) en realidad puedes, con un pequeño truco, ver esta respuesta . El único beneficio de mi enfoque es que ya funcionaba antes de c ++ 11.
Lo que quiero hacer es definir 3 funciones como estas:
template<int t = 0> int test() { return 8; }
template<int t = 1> float test() { return 8.8; }
template<int t = 2> std::string test() { return "8.9"; }
int main()
{
int a = test<0>();
float b = test<1>();
std::string c = test<2>();
return 0;
}
Usan el mismo tipo de parámetro de plantilla pero devuelven diferentes tipos.
Creo que debe haber alguna forma de hacerlo (como
std::get<>()
hace), pero no puedo encontrar cómo hacerlo.
Me parece que buscas la especialización de plantilla de función. La necesidad de proporcionar una implementación diferente para cada una de las llamadas se ajusta a la factura. Sin embargo, hay una advertencia, y es que una especialización puede no alterar la firma de la plantilla primaria que se está especializando, solo la implementación. Esto significa que no podemos hacer, por ejemplo
template<int t> int test(); // Primary
template<> int test<0>() { return 8; } // OK, signature matches
template<> float test<1>() { return 8.8; } // ERROR
Pero aún no estamos tostados. La firma de la especialización debe coincidir con la que obtendrá primaria para un argumento específico. Entonces, si hacemos que el tipo de retorno dependa del parámetro de la plantilla y resolvamos el tipo correcto, podríamos definir nuestra especialización perfectamente.
template<int t> auto test() -> /* Magic involving t that resolves to int, float, string */;
template<> int test<0>() { return 8; }
template<> float test<1>() { return 8.8; }
template<> std::string test<2>() { return "8.9"; }
Algo como eso existe?
Sí, y lo insinuaste.
Podemos usar
std::tuple
.
Tiene una utilidad
std::tuple_element
que puede asignar un entero a una secuencia de tipo (los elementos de la tupla).
Con un poco de ayuda, podemos construir nuestro código para que funcione de la manera que desee:
using types = std::tuple<int, float, std::string>;
template<int t> auto test() -> std::tuple_element_t<t, types>;
template<> int test<0>() { return 8; }
template<> float test<1>() { return 8.8; }
template<> std::string test<2>() { return "8.9"; }
Ahora cada especialización coincide con la firma con la que terminaría el primario. Y así obtenemos la aprobación de nuestro compilador.
y
@formerlyknownas_463035818
han proporcionado una forma de especialización de plantilla bien explicada para hacerlo.
Alternativamente, uno puede combinar las tres funciones en una sola función usando
if-constexpr
y con un
decltype(auto)
en
c ++ 17
.
#include <iostream>
#include <string>
#include <cstring>
using namespace std::literals;
template<int t>
constexpr decltype(auto) test() noexcept
{
if constexpr (t == 0) return 8;
else if constexpr (t == 1) return 8.8f;
else if constexpr (t == 2) return "8.9"s;
}