36

是否可以编写一个类型特征,其值对于所有常见的 STL 结构(例如,、、、vector... set)都为真map

首先,我想编写一个类型特征,它对 a 为真,vector否则为假。我试过这个,但它没有编译:

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

4

11 回答 11

43

看,另一个基于 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等),您应该执行类似的操作

更新。正如@Deduplicator 所指出的,容器可能不符合 AllocatorAwareContainer 要求(例如:)std::array<T, N>。这就是为什么T::allocator_type不需要检查的原因。但是您可以以类似的方式检查任何/所有容器要求。

于 2015-07-03T12:37:11.943 回答
19

实际上,经过反复试验,我发现它很简单:

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

我仍然想知道如何写一个更通用的is_container. 我必须手动列出所有类型吗?

于 2012-08-20T18:31:07.357 回答
18

You would say that it should be simpler than that...

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;
};

... But I am not really sure of whether that is simpler or not.

In C++11 you can use type aliases (I think, untested):

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

The problem with your approach is that the type U is non-deducible in the context where it is used.

于 2012-08-20T18:26:54.727 回答
8

为什么不对 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 等,只需像我上面那样使用部分特化,但仅限于一种容器类型,而不是很多。

于 2012-08-20T22:00:37.660 回答
8

虽然此处尝试猜测类是否为容器的其他答案可能对您有用,但我想向您展示一种替代方法,即命名您想要为其返回 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>您添加到规则中的任何类。

于 2012-08-20T19:06:37.913 回答
3
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 { };
于 2012-08-20T18:32:40.283 回答
3

快进到 2018 年和 C++17,我非常敢于改进 @Frank 的答案

// clang++ prog.cc -Wall -Wextra -std=c++17

 #include <iostream>
 #include <vector>

 namespace dbj {
    template<class T>
      struct is_vector {
        using type = T ;
        constexpr static bool value = false;
   };

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

  // and the two "olbigatory" aliases
  template< typename T>
     inline constexpr bool is_vector_v = is_vector<T>::value ;

 template< typename T>
    using is_vector_t = typename is_vector<T>::type ;

 } // dbj

   int main()
{
   using namespace dbj;
     std::cout << std::boolalpha;
     std::cout << is_vector_v<std::vector<int>> << std::endl ;
     std::cout << is_vector_v<int> << std::endl ;
}   /*  Created 2018 by dbj@dbj.org  */

证明布丁”。有更好的方法可以做到这一点,但这适用于std::vector.

于 2018-06-27T08:34:49.663 回答
3

我喜欢检测某物是否是容器的方法是查找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 {};
于 2017-12-20T20:41:41.613 回答
1

我们也可以使用概念。我用GCC 10.1 flag编译了这个-std=c++20


#include<concepts>

template<typename T>
concept is_container = requires (T a)
{ 
    a.begin(); 
    // Uncomment both lines for vectors only
    // a.data(); // arrays and vectors
    // a.reserve(1); // narrowed down to vectors
    
};

于 2021-10-09T02:53:54.167 回答
0

在我们的项目中,我们仍然没有设法迁移到支持 C++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 {};
于 2017-06-30T14:13:33.013 回答
0

如果您还想让它适用于 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 检查 const 和 volatile 是否也匹配。

于 2017-08-15T07:09:07.007 回答