10

Quiero una plantilla para seleccionar dos tipos en función de alguna condición. P.ej.Rasgos de tipo C++ para seleccionar entre T1 y T2

struct Base {}; 

template <typename T1, typename T2> 
struct test 
{ 
    // e.g. here it should select T1/T2 that is_base_of<Base> 
    typename select_base<T1, T2>::type m_ValueOfBaseType; 
}; 

Por supuesto, para pasar a la condición de select_base (para que sea genérico) sería útil, pero la solución no modificable es más fácil y bueno también.

Aquí es una solución de muestra que he intentado pero siempre selecciona T1: http://ideone.com/EnVT8

La cuestión es cómo implementar la plantilla select_base.

+0

¿Cuál es tu pregunta? – Nawaz

+0

Su pregunta no está clara. Desea seleccionar el tipo de base entre los dos tipos 'T1' o' T2', por ejemplo, si 'T1' deriva de' T2', entonces el tipo de salida debe ser 'T2'. ¿O desea seleccionar el tipo que deriva de un tercer tipo, decir 'base'? – Nawaz

+0

@Nawaz: del ideone vinculado, yo diría que 'Base' está fijo y él quiere elegir cualquiera de 'T1' o' T2' deriva de ello. –

Respuesta

6

C++ 14 (y en adelante):

template <typename T, typename U> 
struct select_base: 
    std::conditional_t<std::is_base_of<T, Base>::value, T, U> {}; 

En el mismo sentido, en su lugar puede utilizar esto:

template<typename T, typename U> 
using select_base = std::conditional_t<std::is_base_of_v<T,Base>, T, U>; 

La diferencia entre estos dos enfoques se puede observar cuando los usas Por ejemplo, en el primer caso si tiene que usar ::type mientras que en el segundo, no lo hace. Y si cualquier tipo dependiente implica el uso del primer enfoque, también debe usar typename para ayudar al compilador. El segundo enfoque está libre de todos esos ruidos y, por lo tanto, es superior al resto de los enfoques en esta respuesta.

Además, tenga en cuenta que también puede escribir alias de tipo similar en C++ 11.


C++ 11:

template <typename T, typename U> 
struct select_base: 
    std::conditional<std::is_base_of<T, Base>::value, T, U>::type {}; 
//    ^          ^~~~~~ 

C++ 98:

condicional es bastante fácil:

template <typename bool, typename T, typename U> 
struct conditional { typedef T type; }; 

template <typename T, typename U> 
struct conditional<false, T, U> { typedef U type; }; 

is_base_of es un poco más complicado, hay una implementación disponible en Boost que no reproduciré aquí.

A continuación, vea C++ 11.

+0

Sí, esto es genérico y funciona bien, ¡gracias! Me olvidé por completo de los parámetros de plantilla sin tipo. – queen3

+0

La plantilla 'if_' se ha estandarizado como' std :: conditional'. http://en.cppreference.com/w/cpp/types/conditional – NicholasM

+0

@NicholasM: respuesta actualizada. –

18

Si utiliza std::conditional en lugar de if_ plantilla de clase que se aplica en @Matthieu en su respuesta, entonces su solución reduciría a esto:

template <typename T, typename U> 
struct select_base 
{ 
    typedef typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type base_type; 
}; 

O simplemente esto:

template <typename T, typename U> 
struct select_base : std::conditional<std::is_base_of<T, Base>::value, T, U> {}; 

que parece aun mejor.

La diferencia entre estas dos soluciones es que en la primera solución le das a un programador de usar nombre al tipo anidado, como yo he dado base_type, mientras que en la segunda solución del tipo anidado es sólo type cuales no se ve tan amigable con los programadores.

Tenga en cuenta que en las dos soluciones anteriores, usted tiene que utilizar el tipo anidado, ya sea como select_base<T,U>::base_type (en la primera solución) o select_base<T,U>::type (en la segunda solución — y debido a eso, si has utilizar como typename usted se ha escrito en la propia pregunta

sin embargo, si en lugar uso de alias plantilla, definido como:.

template<typename T, typename U> 
using base_type = typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type; 

entonces puede utilizar base_type<T,U> sin ningún tipo anidado y typename como:

template <typename T1, typename T2> 
struct test 
{ 
    //typename select_base<T1, T2>::type m_ValueOfBaseType; //ugly! 

    base_type<T1, T2> m_ValueOfBaseType; //better 
}; 

Espero que ayude.

+0

Ah gracias, no puedo entender esto, el nombre es demasiado raro: x (también, a menos que te falte un 'typedef typename' aquí). –

+0

@MatthieuM .: Oh, simplemente olvidé esa parte. – Nawaz

+0

Sí, gracias por la sugerencia, después de la respuesta de Matthieu, estaba seguro de que encontraría algo similar en type_traits, y por supuesto está ahí. Supongo que tengo que leer primero los documentos, porque reinventar las ruedas es divertido pero no productivo. – queen3

Cuestiones relacionadas