2011-10-20 12 views
11

Actualmente estoy haciendo metaprogramación de algunas plantillas. En mi caso, puedo manejar cualquier tipo "iterable", es decir, cualquier tipo para el que exista un typedef foo const_iterator de la misma manera. Intenté utilizar la nueva metaprogramación de plantillas C++ 11 para esto, sin embargo, no pude encontrar un método para detectar si faltaba cierto tipo.detectando typedef en tiempo de compilación (metaprogramación de plantillas)

Porque también necesito activar/desactivar otras especializaciones de plantillas basadas en otras características, actualmente estoy usando una plantilla con dos parámetros, y la segunda se produce a través de std::enable_if. Esto es lo que estoy haciendo actualmente:

template <typename T, typename Enable = void> 
struct Foo{}; // default case is invalid 

template <typename T> 
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{ 
    void do_stuff(){ ... } 
}; 

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

template<typename T> 
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> { 
    void do_stuff(){ ... } 
}; 

yo no era capaz de hacer algo como esto sin la plantilla exists ayudante. Por ejemplo, simplemente haciendo

template<typename T> 
struct Foo<T, typename T::const_iterator> { 
    void do_stuff(){ ... } 
}; 

no funcionó, ya que en aquellos casos en los que se debe utilizar esta especialización, el caso por defecto se crea una instancia no válido en su lugar.

Sin embargo, no pude encontrar este exists en ninguna parte del nuevo estándar C++ 11, que hasta donde yo sé, simplemente está tomando de boost::type_traits para este tipo de cosas. Sin embargo, en el homepage para boost::type_traits no se muestra ninguna referencia a nada que pueda usarse en su lugar.

¿Falta esta funcionalidad o pasé por alto otra forma obvia de lograr el comportamiento deseado?

Respuesta

13

Si simplemente desea si un tipo dado contiene const_iterator, a continuación se muestra una versión simplificada n de su código:

template<typename T> 
struct void_ { typedef void type; }; 

template<typename T, typename = void> 
struct Foo {}; 

template<typename T> 
struct Foo <T, typename void_<typename T::const_iterator>::type> { 
     void do_stuff(){ ... } 
}; 

Ver this answer por alguna explicación de cómo funciona esta técnica.

+3

Quizás deba publicar un enlace a sus preguntas sobre cómo funciona esto. :) Además, parece que te ha gustado esta, ya te lo he sugerido varias veces. – Xeo

+0

@ Xeo, sí, esto es bastante fácil y directo. Pero no recibí tu primera parte. Quizás deberías publicar un enlace a tus preguntas sobre cómo funciona. :) ... ¿quieres decir que al responder debo poner un enlace a mis preguntas anteriores (en lugar del código)? sí mismo) ? Sospecho que no se recomienda en SO. – iammilind

+1

Nono, lo que quise decir es que publicaste un enlace a [tu pregunta donde preguntaste cómo funciona] (http://stackoverflow.com/questions/6543652/different-template-syntax-for-finding-if-argument -is-a-class-or-not), ya que no es realmente obvio al principio. Woops, y solo noté que escribí 'preguntas', solo quería decir una pregunta, por supuesto. – Xeo

7

Puede crear un rasgo has_const_iterator que proporcione un valor booleano y usarlo en la especialización.

Algo como esto podría hacerlo:

template <typename T> 
struct has_const_iterator { 
private: 
    template <typename T1> 
    static typename T1::const_iterator test(int); 
    template <typename> 
    static void test(...); 
public: 
    enum { value = !std::is_void<decltype(test<T>(0))>::value }; 
}; 

Y entonces usted puede especializarse como esto:

template <typename T, 
      bool IsFundamental = std::is_fundamental<T>::value, 
      bool HasConstIterator = has_const_iterator<T>::value> 
struct Foo; // default case is invalid, so no definition! 

template <typename T> 
struct Foo< T, true, false>{ 
    void do_stuff(){// bla } 
}; 

template<typename T> 
struct Foo<T, false, true> { 
    void do_stuff(){//bla} 
}; 
+1

¿Hay una razón por la que eligió ' test (int) 'en lugar de, por ejemplo,' test() '? Solo me aseguro de entender cómo funciona la resolución de sobrecarga aquí. ¡Gracias! – nknight

+2

@nknight: Motivo: hacer que la llamada a 'prueba' sea inequívoca. – erenon

4

Aquí es otra versión de un control de tipo de miembro rasgo:

template<typename T> 
struct has_const_iterator 
{ 
private: 
    typedef char      yes; 
    typedef struct { char array[2]; } no; 

    template<typename C> static yes test(typename C::const_iterator*); 
    template<typename C> static no test(...); 
public: 
    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 
1

Hay varias maneras de hacerlo. En C++ 03, se puede utilizar impulso y enable_if para definir el rasgo (docs, source):

BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator); 

template <typename T, typename Enable = void> 
struct Foo; 

template <typename T> 
struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> { 
    void do_stuff(){ ... } 
}; 

en C++ 11, se puede usar Tick así:

TICK_TRAIT(has_const_iterator) 
{ 
    template<class T> 
    auto require(const T&) -> valid< 
     has_type<typename T::const_iterator> 
    >; 
}; 

template <typename T, typename Enable = void> 
struct Foo; 

template <typename T> 
struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> { 
    void do_stuff(){ ... } 
}; 

también con Tick puede mejorar aún más el rasgo para detectar realmente que el const_iterator es en realidad un iterador, también.Así decimos que definimos un simple is_iterator rasgo de la siguiente manera:

TICK_TRAIT(is_iterator, 
    std::is_copy_constructible<_>) 
{ 
    template<class I> 
    auto require(I&& i) -> valid< 
     decltype(*i), 
     decltype(++i) 
    >; 
}; 

Entonces podemos definir has_const_iterator rasgo para comprobar que el tipo const_iterator coincide con el rasgo is_iterator así:

TICK_TRAIT(has_const_iterator) 
{ 
    template<class T> 
    auto require(const T&) -> valid< 
     has_type<typename T::const_iterator, is_iterator<_>> 
    >; 
}; 
Cuestiones relacionadas