c++ - Devuelve diferentes tipos con un valor de parámetro de plantilla diferente(pero el mismo tipo)




templates template-specialization (2)

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.


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.


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.

Míralo en vivo







return-type