Определите, является ли тип контейнером STL во время компиляции - PullRequest
18 голосов
/ 23 февраля 2012

Я хотел бы написать шаблон, который определит, является ли тип контейнером stl во время компиляции.

У меня есть следующий бит кода:

struct is_cont{};
struct not_cont{};

template <typename T>
struct is_cont { typedef not_cont result_t; };

но я не уверен, как создать необходимые специализации для std::vector<T,Alloc>, deque<T,Alloc>, set<T,Alloc,Comp> и т.д ...

Ответы [ 6 ]

22 голосов
/ 23 февраля 2012

Примечание: следующий код взят из превосходной утилиты pretty-print , написанной @ Kerrek SB (тема на это в stackoverflow).

Отказ от ответственности: я не знаю, разрешено ли мне копировать и вставлять этот код здесь без разрешения оригинального автора.@Kerrek, дай мне знать, если у тебя возникнут проблемы.: -)


Вы можете использовать этот шаблон класса:

  template<typename T> 
  struct is_container : std::integral_constant<bool, has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> 
  { };

Использование:

 std::cout << is_container<std::vector<int>>::value << std::endl; //true
 std::cout << is_container<std::list<int>>::value << std::endl;   //true 
 std::cout << is_container<std::map<int>>::value << std::endl;    //true
 std::cout << is_container<std::set<int>>::value << std::endl;    //true
 std::cout << is_container<int>::value << std::endl;              //false

Обратите внимание, что is_container требуется следующий помощникшаблоны классов:

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);
    typedef T type;
};

template <typename T>
struct has_begin_end
{
    template<typename C> static char (&f(typename std::enable_if<
      std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
      typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

    template<typename C> static char (&f(...))[2];

    template<typename C> static char (&g(typename std::enable_if<
      std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
      typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

    template<typename C> static char (&g(...))[2];

    static bool const beg_value = sizeof(f<T>(0)) == 1;
    static bool const end_value = sizeof(g<T>(0)) == 1;
};
16 голосов
/ 23 февраля 2012

Во-первых, вы определяете свой основной шаблон, в котором по умолчанию будет использоваться элемент false,

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

Затем вы будете определять частичные специализации для типов контейнеров, которые вместо этого имеют значение true.:

template <typename T,typename Alloc>
struct is_cont<std::vector<T,Alloc> > {
  static const bool value = true;
};

Затем для типа X, который вы хотите проверить, используйте его как

if (is_cont<X>::value) { ... } 
12 голосов
/ 29 июня 2015

Многие из уже предложенных решений являются подробными для обнаружения контейнеров STL.
Они фокусируются на характеристиках, которыми обладают все контейнеры, вместо того, чтобы явно указывать, что это за контейнеры.

Если вы хотите создать свои собственные контейнеры и проверить их на истинный тип, я бы порекомендовал другие решения. Если вы хотите проверять только допустимые контейнеры STL, а не контейнеры, подобные STL, рассмотрите возможность использования следующей реализации, поскольку она обеспечивает точное обнаружение контейнера STL:

#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <type_traits>

//specialize a type for all of the STL containers.
namespace is_stl_container_impl{
  template <typename T>       struct is_stl_container:std::false_type{};
  template <typename T, std::size_t N> struct is_stl_container<std::array    <T,N>>    :std::true_type{};
  template <typename... Args> struct is_stl_container<std::vector            <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::deque             <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::list              <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::forward_list      <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::set               <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::multiset          <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::map               <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::multimap          <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_set     <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_multiset<Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_map     <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::unordered_multimap<Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::stack             <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::queue             <Args...>>:std::true_type{};
  template <typename... Args> struct is_stl_container<std::priority_queue    <Args...>>:std::true_type{};
}

//type trait to utilize the implementation type traits as well as decay the type
template <typename T> struct is_stl_container {
  static constexpr bool const value = is_stl_container_impl::is_stl_container<std::decay_t<T>>::value;
};

Обратите внимание на использование std::decay, чтобы избежать неправильного вывода типа на основе определителей типа. Кроме того, мы использовали наследование std::true_type и std::false_type, чтобы избежать указания типов ::type самостоятельно. Шаблоны переменных C ++ 11 используются для определения n параметров типа шаблона, необходимых для построения контейнеров.

Использование реализации соответствует ожиданиям:

  std::cout << std::boolalpha;
  std::cout << is_stl_container<std::vector<int>>::value << '\n';
  std::cout << is_stl_container<std::vector<int>const&>::value << '\n';
  std::cout << is_stl_container<int>::value << '\n';

отпечатки:

true
true
false
10 голосов
/ 01 мая 2013

В соответствии с предположением, что универсальный тест времени компиляции для имеет-stl-container-like-interface будет подходящим решением, этот определяет stl-подобный контейнер T по интерфейсу:

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 &

Дополнительные требования, например, метод size(), могут быть добавлены очевидным образом, или интерфейсы других канонических типов будут проверены во время компиляции очевидным аналогичным образом.

#ifndef IS_STL_CONTAINER_LIKE_H
#define IS_STL_CONTAINER_LIKE_H

#include <type_traits>

template<typename T>
struct is_stl_container_like
{
    typedef typename std::remove_const<T>::type test_type;

    template<typename A>
    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_same<decltype(pt->begin()),iterator>::value &&
                std::is_same<decltype(pt->end()),iterator>::value &&
                std::is_same<decltype(cpt->begin()),const_iterator>::value &&
                std::is_same<decltype(cpt->end()),const_iterator>::value &&
                std::is_same<decltype(**pi),value_type &>::value &&
                std::is_same<decltype(**pci),value_type const &>::value;

    }

    template<typename A>
    static constexpr bool test(...) {
        return false;
    }

    static const bool value = test<test_type>(nullptr);

};

#endif

Вот тестовая программа, созданная с использованием GCC 4.7.2, clang 3.2, Intel C ++ 13.1.1:

#include "is_stl_container_like.h"

// Testing ...

#include <iostream>
#include <vector>
#include <array>
#include <functional>

using namespace std;

template<class C>
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<class C>
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<vector<int> const >::value << endl; // Yes
    cout << is_stl_container_like<array<int,42>>::value << endl; // Yes
    cout << is_stl_container_like<polymorphic<vector<int>>>::value << endl; // Yes
    cout << is_stl_container_like<function<int(int)>>::value << endl; // No
    cout << is_stl_container_like<int>::value << endl; // No
    cout << is_stl_container_like<reject<vector<int>>>::value << endl; //No
}
2 голосов
/ 31 октября 2012

В ускорении is_container http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html

is_container<C>::type --- Результат метафункции, которая оценивается как mpl :: true_, если данный тип, C, должен рассматриваться как контейнер, mpl :: false_ в противном случае. Обычно любая реализация is_container должна вести себя так, как если бы она была логической константой MPL.

1 голос
/ 05 июня 2016

Этот код определяет черты для контейнера. Это из библиотеки prettyprint:

//put this in type_utils.hpp 
#ifndef commn_utils_type_utils_hpp
#define commn_utils_type_utils_hpp

#include <type_traits>
#include <valarray>

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 <typename T>
        struct has_const_iterator : private sfinae_base
        {
        private:
            template <typename C> static yes & test(typename C::const_iterator*);
            template <typename C> static no  & test(...);
        public:
            static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
            using type =  T;

            void dummy(); //for GCC to supress -Wctor-dtor-privacy
        };

        template <typename T>
        struct has_begin_end : private sfinae_base
        {
        private:
            template <typename C>
            static yes & f(typename std::enable_if<
                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
                             typename C::const_iterator(C::*)() const>::value>::type *);

            template <typename C> static no & f(...);

            template <typename C>
            static yes & g(typename std::enable_if<
                std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
                             typename C::const_iterator(C::*)() const>::value, void>::type*);

            template <typename C> static no & g(...);

        public:
            static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
            static bool const end_value = sizeof(g<T>(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 <typename T>
    struct is_container : public std::integral_constant<bool,
                                                        detail::has_const_iterator<T>::value &&
                                                        detail::has_begin_end<T>::beg_value  &&
                                                        detail::has_begin_end<T>::end_value> { };

    template <typename T, std::size_t N>
    struct is_container<T[N]> : std::true_type { };

    template <std::size_t N>
    struct is_container<char[N]> : std::false_type { };

    template <typename T>
    struct is_container<std::valarray<T>> : std::true_type { };

    template <typename T1, typename T2>
    struct is_container<std::pair<T1, T2>> : std::true_type { };

    template <typename ...Args>
    struct is_container<std::tuple<Args...>> : std::true_type { };

}}  //namespace
#endif

Подробнее см. в моем блоге .

Смежный вопрос: шаблон класса c ++; функция с произвольным типом контейнера, как его определить?

...