【问题标题】:How to write a type trait `is_container` or `is_vector`?如何编写类型特征`is_container`或`is_vector`?
【发布时间】:2012-08-16 02:06:50
【问题描述】:

是否可以为所有常见的 STL 结构(例如,vectorsetmap、...)编写一个值为 true 的类型特征?

首先,我想编写一个类型特征,对于 vector 为 true,否则为 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

【问题讨论】:

    标签: c++ templates sfinae enable-if


    【解决方案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::vectorstd::list 等),您应该执行类似 this 的操作。

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

    【讨论】:

    • is_cointainer_helper 现在可以用 std::void_t 代替
    • 对于is_container 结构,void 用作第二个参数。经过测试,它似乎适用于任何类型,如果这是相同的类型:template&lt;typename T, typename _ = int&gt;is_container_helper&lt;...&gt;, int&gt;。为什么这样?如果我们使用voidint,或者intvoid,为什么它不起作用?
    • 因为您确实使用了is_container&lt; Type &gt;,即is_container&lt; Type, void &gt;,它不是专门的,并且使用了默认实现:std::false_type
    • 一个 Container 可能不是一个 AllocatorAwareContainer
    • 使用std::void_t you can trash std::conditional_t?
    【解决方案2】:

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

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

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

    【讨论】:

    • +1...呵呵! (也许您还想在此处添加allocator_type,其特征实际上是is_vector_with_default_allocator
    • @AndrewDurward:谢谢,已修复。
    • 6 年后:) 你愿意发布一个链接到一些你放置了一个最小用法示例的在线 ide 吗?谢谢
    • @NoSenseEtAl 这是错误的代码。自动 isv = is_vector::value ;是非模板结构'is_vector'的显式特化...检查
    • 你需要先假的通用模板wandbox.org/permlink/76dOGKSuvEiBdAdw
    【解决方案3】:

    你会说它应该比那更简单......

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

    ...但我不确定这是否更简单

    在 C++11 中,您可以使用类型别名(我认为,未经测试):

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

    您的方法的问题是U 类型在使用它的上下文中是不可推导的。

    【讨论】:

    • 对于is_container?只是测试它是否有allocator_type?
    • @rhalbersma:问题是你对is_container 的定义是什么……我的手卷single_list 是一个容器吗? std::string 是一个容器吗?它具有分配器的事实是否使对象成为容器?
    • 对于is_container,很多人只是寻找beginend 函数,然后使用is_iterable,这是(大多数时候)你真正需要知道的。
    • @MooingDuck:我在哪里可以找到is_iterable
    • 值得注意的是,未经测试的“使用”语法在这种情况下效果不佳。它将尝试强制实例化依赖类型“T::value_type”,如果 T 类似于 int,则会因编译错误而失败。
    【解决方案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 {};
    

    等等。

    您需要包含&lt;type_traits&gt; 以及您添加到规则中的任何类。

    【讨论】:

      【解决方案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 等,只需要像我上面做的那样使用部分特化,但仅限于一种容器类型,而不是很多。

      【讨论】:

        【解决方案6】:

        我喜欢检测某物是否是容器的方法是查找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 {};
        

        【讨论】:

        • 不错的特技。但这也会将std::string(例如)“制作”成一个容器。使用上述:is_container&lt;std::string&gt;::valuetrue
        • 直到现在我还相信std::string 不是标准容器?虽然,我同意this text
        【解决方案7】:
        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 { };
        

        【讨论】:

        • 说:错误 C2146:语法错误:缺少 ';'在 Visual Studio 2012 中的标识符“测试”之前
        • VC 不知道 uint8_t
        • #include 将允许它。
        【解决方案8】:

        快进到 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  */
        

        "proof the pudding"。有更好的方法可以做到这一点,但这适用于std::vector

        【讨论】:

          【解决方案9】:

          我们也可以使用概念。我用 GCC 10.1 标志 -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
              
          };
          
          

          【讨论】:

            【解决方案10】:

            在我们的项目中,我们仍然无法迁移到支持 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 {};
            

            【讨论】:

              【解决方案11】:

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

              【讨论】:

              • 添加了 std::decay 以支持引用
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-04-10
              • 1970-01-01
              • 2016-04-10
              相关资源
              最近更新 更多