Подтвердить что ты не робот

Как написать черту типа `is_container` или` is_vector`?

Можно ли написать черту типа, значение которой истинно для всех общих структур STL (например, vector, set, map,...)?

Чтобы начать работу, я хотел бы написать черту типа, которая верна для vector и false в противном случае. Я пробовал это, но он не компилируется:

template<class T, typename Enable = void>
struct is_vector {
  static bool const value = false;
};

template<class T, class U>
struct is_vector<T, typename boost::enable_if<boost::is_same<T, std::vector<U> > >::type> {
  static bool const value = true;
};

Сообщение об ошибке template parameters not used in partial specialization: U.

4b9b3361

Ответ 1

Посмотрите еще одно решение на основе SFINAE для обнаружения контейнеров, подобных STL:

template<typename T, typename _ = void>
struct is_container : std::false_type {};

template<typename... Ts>
struct is_container_helper {};

template<typename T>
struct is_container<
        T,
        std::conditional_t<
            false,
            is_container_helper<
                typename T::value_type,
                typename T::size_type,
                typename T::allocator_type,
                typename T::iterator,
                typename T::const_iterator,
                decltype(std::declval<T>().size()),
                decltype(std::declval<T>().begin()),
                decltype(std::declval<T>().end()),
                decltype(std::declval<T>().cbegin()),
                decltype(std::declval<T>().cend())
                >,
            void
            >
        > : public std::true_type {};

Конечно, вы можете изменить методы и типы, которые нужно проверить.

Если вы хотите обнаружить только контейнеры STL (это означает std::vector, std::list и т.д.), вы должны сделать что-то вроде .

Ответ 2

Вы сказали бы, что это должно быть проще...

template <typename T, typename _ = void>
struct is_vector { 
    static const bool value = false;
};
template <typename T>
struct is_vector< T,
                  typename enable_if<
                      is_same<T,
                              std::vector< typename T::value_type,
                                           typename T::allocator_type >
                             >::value
                  >::type
                >
{
    static const bool value = true;
};

... Но я не уверен, проще ли это или нет.

В С++ 11 вы можете использовать псевдонимы типов (я думаю, непроверенные):

template <typename T>
using is_vector = is_same<T, std::vector< typename T::value_type,
                                          typename T::allocator_type > >;

Проблема с вашим подходом заключается в том, что тип U не выводится в том контексте, в котором он используется.

Ответ 3

Собственно, после некоторых проб и ошибок я нашел это довольно просто:

template<class T>
struct is_vector<std::vector<T> > {
  static bool const value = true;
};

Мне бы хотелось узнать, как написать более общий is_container. Должен ли я перечислить все типы вручную?

Ответ 4

В то время как другие ответы здесь, которые пытаются угадать, является ли класс контейнером или нет, могут работать для вас, я хотел бы представить вам альтернативу именования типа, для которого вы хотите вернуть true. Вы можете использовать это для создания произвольных типов типов is_(something).

template<class T> struct is_container : public std::false_type {};

template<class T, class Alloc> 
struct is_container<std::vector<T, Alloc>> : public std::true_type {};

template<class K, class T, class Comp, class Alloc> 
struct is_container<std::map<K, T, Comp, Alloc>> : public std::true_type {};

И так далее.

Вам нужно будет включить <type_traits> и любые классы, которые вы добавите в свои правила.

Ответ 5

Почему бы не сделать что-то подобное для is_container?

template <typename Container>
struct is_container : std::false_type { };

template <typename... Ts> struct is_container<std::list<Ts...> > : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...> > : std::true_type { };
// ...

Таким образом, пользователи могут добавлять свои собственные контейнеры, частично специализируясь. Что касается is_vector et al, просто используйте частичную специализацию, как я сделал выше, но ограничиваем ее только одним типом контейнера, не так много.

Ответ 6

template <typename T>
struct is_container {

    template <
       typename U,
       typename I = typename U::const_iterator
    >   
    static int8_t      test(U* u); 

    template <typename U>
    static int16_t     test(...);

    enum { value  =  sizeof test <typename std::remove_cv<T>::type> (0) == 1 };
};


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

Ответ 7

Способ, которым я хотел бы определить, является ли контейнер контейнером, - это искать функции-члены data() и size(). Вот так:

template <typename T, typename = void>
struct is_container : std::false_type {};

template <typename T>
struct is_container<T
   , std::void_t<decltype(std::declval<T>().data())
      , decltype(std::declval<T>().size())>> : std::true_type {};

Ответ 8

В нашем проекте нам все еще не удалось перейти к компилятору, поддерживающему С++ 11, поэтому для type_traits объектов контейнера мне пришлось написать простой помощник стиля boost:

template<typename Cont> struct is_std_container: boost::false_type {};
template<typename T, typename A> 
struct is_std_container<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_container<std::list<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_container<std::deque<T,A> >: boost::true_type {};
template<typename K, typename C, typename A> 
struct is_std_container<std::set<K,C,A> >: boost::true_type {};
template<typename K, typename T, typename C, typename A> 
struct is_std_container<std::map<K,T,C,A> >: boost::true_type {};

template<typename Cont> struct is_std_sequence: boost::false_type {};
template<typename T, typename A> 
struct is_std_sequence<std::vector<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_sequence<std::list<T,A> >: boost::true_type {};
template<typename T, typename A> 
struct is_std_sequence<std::deque<T,A> >: boost::true_type {};

Ответ 9

Если вы также хотите заставить его работать для const std::vector, вы можете использовать следующее:

namespace local {

template<typename T, typename _ = void>
struct isVector: std::false_type {
};

template<typename T>
struct isVector<T,
        typename std::enable_if<
                std::is_same<typename std::decay<T>::type, std::vector<typename std::decay<T>::type::value_type, typename std::decay<T>::type::allocator_type> >::value>::type> : std::true_type {
};

}

TEST(TypeTraitTest, testIsVector) {
    ASSERT_TRUE(local::isVector<std::vector<int>>::value);
    ASSERT_TRUE(local::isVector<const std::vector<int>>::value);

    ASSERT_FALSE(local::isVector<std::list<int>>::value);
    ASSERT_FALSE(local::isVector<int>::value);

    std::vector<uint8_t> output;
    std::vector<uint8_t> &output2 = output;
    EXPECT_TRUE(core::isVector<decltype(output)>::value);
    EXPECT_TRUE(core::isVector<decltype(output2)>::value);
}

Без вызова std:: remove_cv второй ASSERT_TRUE завершится с ошибкой. Но, конечно, это зависит от ваших потребностей. Дело в том, что согласно спецификациям, std:: is_same проверяет наличие констант и volatile, чтобы они также совпадали.