【问题标题】:Detect whether type is associative container检测类型是否为关联容器
【发布时间】:2014-03-03 20:41:15
【问题描述】:

我正在编写一些容器操作函数。通常情况下,有一个版本用于类似矢量的容器,如 vector、list、deque、array 等,而另一个版本用于关联容器,如 map、multimap、unordered_map 等。我想知道什么是“最好的方法”来检测一个类是否是一个关联容器。也许像用BOOST_MPL_HAS_XXX_TRAIT_DEF 检测mapped_type typedef 的存在?

【问题讨论】:

    标签: c++ templates containers sfinae


    【解决方案1】:

    这是一个编译时测试,因此没有 CPU/内存效率方面来选择“最佳方式”。如果您通过使用 boost 检查 mapped_type 使其工作,并且这符合您的需求,则没有理由寻找任何不同的东西,尽管肯定有无 boost 的替代品(例如,参见 here

    请注意,尽管 setunordered_set 被标准视为关联容器,但没有 mapped_type 成员 - 如果您想包含它们,您可以测试 key_type

    【讨论】:

    • 我猜我所说的“最佳”是指一种分离效果最好的方法。正如您所指出的,例如,检查 mapped_type 并没有得到标准中的所有关联容器。另外,我认为可能还有其他元函数来检查关联容器的行为,这些关联容器可以在非 stl 容器上工作,这些容器具有所有正确的接口作为没有 typedef 的关联容器。也许那是“更笼统”?
    • 那么你需要选择你将要编程的API......例如,你是否关心有begin/end进行迭代?或者只是拥有operator[](const key_type&)countsize() 等也是如此。答案可能取决于您要编写的容器操作函数,但您还没有告诉我们。
    • 这很公平。我想根据key_type 的存在来决定就足够了。
    【解决方案2】:

    我不会这样假设。具体并专门化模板。

    我这样做:

    // is_deque
    // ========
    
    template<typename T, typename ... Types>
    struct is_deque {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_deque<std::deque<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // is_forward_list
    // ===============
    
    template<typename T, typename ... Types>
    struct is_forward_list {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_forward_list<std::forward_list<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // list
    // ====
    
    template<typename T, typename ... Types>
    struct is_list {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_list<std::list<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // vector
    // ======
    
    template<typename T, typename ... Types>
    struct is_vector {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_vector<std::vector<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // map
    // ===
    
    template<typename T, typename ... Types>
    struct is_map {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_map<std::map<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // set
    // ===
    
    template<typename T, typename ... Types>
    struct is_set {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_set<std::set<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // unordered_map
    // =============
    
    template<typename T, typename ... Types>
    struct is_unordered_map {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_unordered_map<std::unordered_map<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // unordered_set
    // =============
    
    template<typename T, typename ... Types>
    struct is_unordered_set {
        static constexpr bool value = false;
    };
    
    
    template<typename ... Types>
    struct is_unordered_set<std::unordered_set<Types...>> {
        static constexpr bool value = true;
    };
    
    
    // is_sequence_container
    // =====================
    
    template <typename T>
    struct is_sequence_container {
        static constexpr bool value
            =  is_deque<T>::value
            || is_forward_list<T>::value
            || is_list<T>::value
            || is_vector<T>::value;
    };
    
    
    // is_associative_container
    // ========================
    
    template <typename T>
    struct is_associative_container {
        static constexpr bool value
            =  is_map<T>::value
            || is_set<T>::value;
    };
    
    
    // is_unordered_associative_container
    // ==================================
    
    template <typename T>
    struct is_unordered_associative_container {
        static constexpr bool value
            =  is_unordered_map<T>::value
            || is_unordered_set<T>::value;
    };
    
    
    // is_container
    // ============
    
    template <typename T>
    struct is_container {
        static constexpr bool value
            =  is_sequence_container<T>::value
            || is_associative_container<T>::value
            || is_unordered_associative_container<T>::value;
    };
    

    【讨论】:

      【解决方案3】:

      我知道这个问题是 5 年前提出的,但这是我所做的,除了 c++11 之外没有任何要求:

      /// @brief container traits
      ////////////////////////////////////////////////////////////////////////////////
      
      namespace container_traits {
      
      using tc = char[2];
      
      template<typename T> struct is_container {
        static tc& test(...);
      
        template <typename U>
        static char test(U&&, decltype(std::begin(std::declval<U>()))* = 0);
        static constexpr bool value = sizeof(test(std::declval<T>())) == 1;
      };
      
      template < typename T > struct is_associative {
        static tc& test(...) ;
      
        template < typename U >
        static char test(U&&, typename U::key_type* = 0) ;
        static constexpr bool value = sizeof( test( std::declval<T>() ) ) == 1 ;
      };
      
      }
      
      template < typename T > struct is_container :
        std::conditional<(container_traits::is_container<T>::value || std::is_array<T>::value)
                         && !std::is_same<T, std::string>::value
                         && !std::is_same<T, const std::string>::value, std::true_type, std::false_type >::type {};
      
      template < typename T > struct is_associative :
        std::conditional< container_traits::is_container<T>::value && container_traits::is_associative<T>::value,  std::true_type, std::false_type >::type {};
      
      
      
      ////////////////////////////////////////////////////////////////////////////////
      /// @brief no std::enable_if_t in c++11
      ////////////////////////////////////////////////////////////////////////////////
      
      #if __cplusplus <= 201103L
      namespace std {
      template< bool B, class T = void >
      using enable_if_t = typename std::enable_if<B,T>::type;
      }
      #endif
      

      【讨论】:

        猜你喜欢
        • 2018-05-16
        • 1970-01-01
        • 1970-01-01
        • 2023-01-24
        • 2019-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-02
        相关资源
        最近更新 更多