Determinar si un tipo es un contenedor STL en tiempo de comstackción

Me gustaría escribir una plantilla que determinará si un tipo es un contenedor stl en tiempo de comstackción.

Tengo el siguiente código:

struct is_cont{}; struct not_cont{}; template  struct is_cont { typedef not_cont result_t; }; 

pero no estoy seguro de cómo crear las especializaciones necesarias para std::vector, deque, set etc …

    Primero, define su plantilla principal, que tendrá un miembro que es falso en el caso predeterminado:

     template  struct is_cont { static const bool value = false; }; 

    Luego, definirá las especializaciones parciales para sus tipos de contenedor que tienen un valor de verdadero en su lugar:

     template  struct is_cont > { static const bool value = true; }; 

    Luego, para un tipo X que quiera verificar, úselo como

     if (is_cont::value) { ... } 

    Nota: el siguiente código se toma de una excelente utilidad llamada pretty-print escrita por @Kerrek SB (un tema sobre ella en stackoverflow).

    Descargo de responsabilidad: no sé si puedo copiar y pegar este código aquí sin tener el permiso del autor original. @Kerrek, avíseme si tiene algún problema. 🙂


    Puedes usar esta plantilla de clases:

      template struct is_container : std::integral_constant::value && has_begin_end::beg_value && has_begin_end::end_value> { }; 

    Uso:

      std::cout < < is_container>::value < < std::endl; //true std::cout << is_container>::value < < std::endl; //true std::cout << is_container>::value < < std::endl; //true std::cout << is_container>::value < < std::endl; //true std::cout << is_container::value < < std::endl; //false 

    Tenga en cuenta que is_container necesita las siguientes plantillas de clase auxiliar:

     template struct has_const_iterator { private: typedef char yes; typedef struct { char array[2]; } no; template static yes test(typename C::const_iterator*); template static no test(...); public: static const bool value = sizeof(test(0)) == sizeof(yes); typedef T type; }; template  struct has_begin_end { template static char (&f(typename std::enable_if< std::is_same(&C::begin)), typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; template static char (&f(...))[2]; template static char (&g(typename std::enable_if< std::is_same(&C::end)), typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; template static char (&g(...))[2]; static bool const beg_value = sizeof(f(0)) == 1; static bool const end_value = sizeof(g(0)) == 1; }; 

    Muchas de las soluciones ya propuestas son detalladas para detectar contenedores STL.
    Se centran en las características que poseen todos los contenedores, en lugar de indicar explícitamente qué son los contenedores.

    Si quisiera crear sus propios contenedores y hacerlos evaluar con un tipo verdadero, recomendaría las otras soluciones. Si solo desea validar los contenedores STL legítimos y no los contenedores similares a STL, considere usar la siguiente implementación, ya que proporciona una detección precisa del contenedor STL:

     #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  //specialize a type for all of the STL containers. namespace is_stl_container_impl{ template  struct is_stl_container:std::false_type{}; template  struct is_stl_container> :std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; template  struct is_stl_container>:std::true_type{}; } //type trait to utilize the implementation type traits as well as decay the type template  struct is_stl_container { static constexpr bool const value = is_stl_container_impl::is_stl_container>::value; }; 

    Tenga en cuenta el uso de std::decay para evitar la deducción de tipo incorrecta basada en calificadores de tipo. Además, hemos utilizado heredar std::true_type y std::false_type para evitar especificar los tipos ::type tipos mismos. Las plantillas variadicas de C ++ 11 se usan para deducir la cantidad n de parámetros de tipo de plantilla necesarios para construir los contenedores.

    Usar la implementación es como era de esperar:

      std::cout < < std::boolalpha; std::cout << is_stl_container>::value < < '\n'; std::cout << is_stl_containerconst&>::value < < '\n'; std::cout << is_stl_container::value < < '\n'; 

    huellas dactilares:

     true true false 

    Siguiendo la sugerencia de que una prueba genérica en tiempo de comstackción para has-an-stl-container-like-interface sería una solución apropiada, este define un contenedor tipo stl T por la interfaz:

     T::iterator T::begin(); T::iterator T::end(); T::const_iterator T::begin() const; T::const_iterator T::end() const; *T::iterator is T::value_type & *T::const_iterator is T::value_type const & 

    Se pueden agregar requisitos adicionales, por ejemplo, un método de size() , de una manera obvia, u otras interfaces de tipo canónico probadas en tiempo de comstackción de una manera obvia similar.

     #ifndef IS_STL_CONTAINER_LIKE_H #define IS_STL_CONTAINER_LIKE_H #include  template struct is_stl_container_like { typedef typename std::remove_const::type test_type; template static constexpr bool test( A * pt, A const * cpt = nullptr, decltype(pt->begin()) * = nullptr, decltype(pt->end()) * = nullptr, decltype(cpt->begin()) * = nullptr, decltype(cpt->end()) * = nullptr, typename A::iterator * pi = nullptr, typename A::const_iterator * pci = nullptr, typename A::value_type * pv = nullptr) { typedef typename A::iterator iterator; typedef typename A::const_iterator const_iterator; typedef typename A::value_type value_type; return std::is_samebegin()),iterator>::value && std::is_sameend()),iterator>::value && std::is_samebegin()),const_iterator>::value && std::is_sameend()),const_iterator>::value && std::is_same::value && std::is_same::value; } template static constexpr bool test(...) { return false; } static const bool value = test(nullptr); }; #endif 

    Aquí hay un progtwig de prueba, construido con GCC 4.7.2, clang 3.2, Intel C ++ 13.1.1:

     #include "is_stl_container_like.h" // Testing ... #include  #include  #include  #include  using namespace std; template struct polymorphic : private C { typedef typename C::value_type value_type; typedef typename C::iterator iterator; typedef typename C::const_iterator const_iterator; virtual ~polymorphic(){} virtual const_iterator begin() const { return C::begin(); } virtual iterator begin() { return C::begin(); } virtual const_iterator end() const { return C::end(); } virtual iterator end() { return C::end(); } }; template struct reject : private C { typedef typename C::value_type value_type; typedef typename C::iterator iterator; typedef typename C::const_iterator const_iterator; const_iterator begin() { return C::begin(); } iterator begin() const { return C::begin(); } const_iterator end() { return C::end(); } iterator end() const { return C::end(); } }; int main() { cout < < is_stl_container_like const >::value < < endl; // Yes cout << is_stl_container_like>::value < < endl; // Yes cout << is_stl_container_like>>::value < < endl; // Yes cout << is_stl_container_like>::value < < endl; // No cout << is_stl_container_like::value < < endl; // No cout << is_stl_container_like>>::value < < endl; //No } 

    Hay is_container en boost http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html

    is_container::type — Resultado de la metafunción que evalúa a mpl :: true_ si un tipo dado, C, debe tratarse como un contenedor, mpl :: false_ de lo contrario Generalmente, cualquier implementación de is_container necesita comportarse como si fuera una constante booleana MPL ..

    Este código define los rasgos para el contenedor. Es originaria de la biblioteca de prettyprint:

     //put this in type_utils.hpp #ifndef commn_utils_type_utils_hpp #define commn_utils_type_utils_hpp #include  #include  namespace common_utils { namespace type_utils { //from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp //also see https://gist.github.com/louisdx/1076849 namespace detail { // SFINAE type trait to detect whether T::const_iterator exists. struct sfinae_base { using yes = char; using no = yes[2]; }; template  struct has_const_iterator : private sfinae_base { private: template  static yes & test(typename C::const_iterator*); template  static no & test(...); public: static const bool value = sizeof(test(nullptr)) == sizeof(yes); using type = T; void dummy(); //for GCC to supress -Wctor-dtor-privacy }; template  struct has_begin_end : private sfinae_base { private: template  static yes & f(typename std::enable_if< std::is_same(&C::begin)), typename C::const_iterator(C::*)() const>::value>::type *); template  static no & f(...); template  static yes & g(typename std::enable_if< std::is_same(&C::end)), typename C::const_iterator(C::*)() const>::value, void>::type*); template  static no & g(...); public: static bool const beg_value = sizeof(f(nullptr)) == sizeof(yes); static bool const end_value = sizeof(g(nullptr)) == sizeof(yes); void dummy(); //for GCC to supress -Wctor-dtor-privacy }; } // namespace detail // Basic is_container template; specialize to derive from std::true_type for all desired container types template  struct is_container : public std::integral_constant::value && detail::has_begin_end::beg_value && detail::has_begin_end::end_value> { }; template  struct is_container : std::true_type { }; template  struct is_container : std::false_type { }; template  struct is_container> : std::true_type { }; template  struct is_container> : std::true_type { }; template  struct is_container> : std::true_type { }; }} //namespace #endif 

    Para más explicación, mira mi publicación en el blog .

    Pregunta relacionada: clase de plantilla c ++; función con tipo de contenedor arbitrario, ¿cómo definirlo?