2010-10-20 9 views
7

necesito para definir una estructura de plantilla tal que:decisión de tipo basado en la existencia de typedef anidada

element<T>::type 

es de tipo:

T::element_type 

si T contiene un (público) ELEMENT_TYPE typedef llamado, de otro modo (si no contiene tales typedef)

element<T>::type 

es de tipo

T::value_type 

si T es mutable y de tipo

const T::value_type 

si T es constante.

Realmente estoy luchando con esto, cualquier sugerencia es muy apreciada! :)

¡Muchas gracias por su ayuda con anticipación!

+0

Para la primera parte se puede leer sobre SFINAE (google) y que está obligado a encontrar ejemplos que ilustran todo lo que necesita –

+2

artículo de Wikipedia en SFINAE en realidad incluye un ejemplo sobre la comprobación de un typedef, escrito realmente por los tuyos. : P – GManNickG

+0

No hay forma de detectar que el typedef, si existe, es público. El Estándar prohíbe específicamente que SFINAE trabaje con la violación del calificador de acceso. – Potatoswatter

Respuesta

4

Tal vez algo como:

template <typename T> 
struct has_element_type 
{ 
    typedef char yes[1]; 
    typedef char no[2]; 

    template <typename C> 
    static yes& test(typename C::element_type*); 

    template <typename> 
    static no& test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

template <typename T> 
struct is_const 
{ 
    static const bool value = false; 
}; 


template <typename T> 
struct is_const<const T> 
{ 
    static const bool value = true; 
}; 

template <typename, bool> // true -> const 
struct value_type_switch; 

template <typename T> 
struct value_type_switch<T, true> 
{ 
    typedef const typename T::value_type type; 
}; 

template <typename T> 
struct value_type_switch<T, false> 
{ 
    typedef typename T::value_type type; 
}; 

template <typename, bool> // true -> has element_type 
struct element_type_switch; 

template <typename T> 
struct element_type_switch<T, true> 
{ 
    typedef typename T::element_type type; 
}; 


template <typename T> 
struct element_type_switch<T, false> 
{ 
    typedef typename value_type_switch<T, is_const<T>::value>::type type; 
}; 

template <typename T> 
struct element 
{ 
    typedef typename element_type_switch<T, 
            has_element_type<T>::value>::type type; 
}; 

Esto debe por supuesto ser dividido y organizado.

+0

en el elemento struct no debería ser 'typedef typename element_type_switch :: value> :: type type;' en lugar de 'typedef typename element_type_switch :: value> type;'? lo mismo en element_type_switch ... –

+0

@Armen: Ya, typo. Gracias. – GManNickG

+0

¡Lo probé y funciona! Muchas gracias. Tengo una pequeña preferencia por la solución propuesta por Potatoswapper (más concisa y * para mí * más fácil de entender), pero tengo problemas para compilarla ... Gracias a todos, lástima que no pueda asignarles una respuesta a ustedes dos. porque lo merecerías! :) – stepelu

1

Como alternativa a la declaración de una clase de rasgos utilizando SFINAE, puede usarla de forma más sutil con especialización parcial.

template< typename T > 
struct empty { // support class is like stripped-down enable_if 
    typedef void type; 
}; 

template< class T, typename v = void > // v is always void! 
struct element { 
    typedef typename T::value_type type; 
}; 

template< class T, typename v > 
struct element< T const, v > { 
    typedef typename T::value_type const type; 
}; 

template< class T > // T in deduced context, T::element_type is SFINAE: 
struct element< T, typename empty< typename T::element_type >::type > { 
    typedef typename T::element_type type; 
}; 

... es posible que desee agregar otro caso para hacer element_type const para const T? Desafortunadamente, esto no funciona en GCC, aunque Comeau lo acepta.

template< class T > 
struct element< T const, typename empty< typename T::element_type >::type > { 
    typedef typename T::element_type const type; 
}; 

Código que utiliza para probar esto:

struct has_et { 
    typedef int element_type; 
}; 

struct has_vt { 
    typedef char value_type; 
}; 

char c; 
int i; 

element<has_vt>::type *cp = &c; 
element<has_et>::type *ip = &i; 
+0

Para aquellos interesados ​​en el error, http://gcc.gnu.org/bugzilla/show_bug.cgi?id = 46105 – Potatoswatter

+0

Intenté esto (con VS2010) pero tengo un problema: funciona perfectamente para el elemento , elemento y elemento , pero no se compila el elemento (con y sin la última especialización adicional) quejándose de más de 1 especialización parcial coincidente. Me gustaría que el elemento :: type sea has_et :: element_type. Gracias de nuevo por su ayuda, ¡muy apreciada! ¿Crees que este problema puede ser resuelto? – stepelu

+0

@KRao: Hmm, traté de manipularlo y no puedo hacer que GCC o Comeau acepten 'const has_et' como argumento. Supongo que debería haber probado mejor: vP. De hecho, se requiere la especialización "adicional" para ser correctos, pero hasta donde sé, debería funcionar correctamente siempre que lo incluya. (Puede borrar el 'const' en su typedef, o hacer que se derive de la especialización non-const, pero debe existir.) Tal vez debería poner este código en una nueva pregunta de SO, ya que estoy tan seguro de que es correcto ... Mientras tanto, creo que la solución de GMan es la elección segura. – Potatoswatter

Cuestiones relacionadas