代码之家  ›  专栏  ›  技术社区  ›  Daniel

检查类型是否为映射

  •  15
  • Daniel  · 技术社区  · 9 年前

    我有时会发现需要编写可应用于对象容器或此类容器的映射的常规例程(即处理映射中的每个容器)。一种方法是为映射类型编写单独的例程,但我认为使用一个可以同时用于两种类型的输入的例程会更自然、更不冗长:

    template <typename T>
    auto foo(const T& items)
    { 
        return foo(items, /* tag dispatch to map or non-map */);
    }
    

    什么是安全、干净的方式来发送标签?

    3 回复  |  直到 9 年前
        1
  •  21
  •   T.C. Yksisarvinen    9 年前

    现有的答案测试非常具体的属性 std::map ,或者说它正是 标准::映射 (对于 std::unordered_map 或具有相同接口的非标准类型 标准::映射 ),或测试其 value_type 正是 std::pair<const key_type, mapped_type> (对于 multimap unordered_map ,但对于具有类似接口的非标准类型为false)。

    这只测试它提供的 key_type mapped_type 成员,可以使用 operator[] ,所以不要这么说 std::multimap 是mappish:

    #include <type_traits>
    
    namespace detail {
      // Needed for some older versions of GCC
      template<typename...>
        struct voider { using type = void; };
    
      // std::void_t will be part of C++17, but until then define it ourselves:
      template<typename... T>
        using void_t = typename voider<T...>::type;
    
      template<typename T, typename U = void>
        struct is_mappish_impl : std::false_type { };
    
      template<typename T>
        struct is_mappish_impl<T, void_t<typename T::key_type,
                                         typename T::mapped_type,
                                         decltype(std::declval<T&>()[std::declval<const typename T::key_type&>()])>>
        : std::true_type { };
    }
    
    template<typename T>
    struct is_mappish : detail::is_mappish_impl<T>::type { };
    

    因为 is_mappish 具有以下“基本特征”之一 true_type false_type 你可以这样发送:

    template <typename T>
    auto foo(const T& items, true_type)
    {
        // here be maps
    }
    
    template <typename T>
    auto foo(const T& items, false_type)
    {
        // map-free zone
    }
    
    template <typename T>
    auto foo(const T& items)
    { 
        return foo(items, is_mappish<T>{});
    }
    

    或者你可以完全避免调度,而只是超载 foo 对于地图和非地图:

    template <typename T,
              std::enable_if_t<is_mappish<T>{}, int> = 0>
    auto foo(const T& items)
    {
        // here be maps
    }
    
    template <typename T,
              std::enable_if_t<!is_mappish<T>{}, int> = 0>
    auto foo(const T& items)
    {
        // map-free zone
    }
    
        2
  •  12
  •   matsjoyce    9 年前

    这对我来说是有效的,但并没有经过100%的测试:

    template <class T>
    struct isMap {
        static constexpr bool value = false;
    };
    
    template<class Key,class Value>
    struct isMap<std::map<Key,Value>> {
        static constexpr bool value = true;
    };
    
    int main() {
        constexpr bool b1 = isMap<int>::value; //false
        constexpr bool b2 = isMap<std::vector<int>>::value; //false
        constexpr bool b3 = isMap<std::map<int,std::string>>::value; //true
        constexpr bool b4 = isMap<std::future<int>>::value; //false
    }
    
        3
  •  2
  •   Daniel    9 年前

    下面是我想到的:

    #include <type_traits>
    #include <utility>
    
    namespace detail
    {
        template <typename T, typename = void>
        struct IsMap : std::false_type {};
    
        template <typename T>
        struct IsMap<T, std::enable_if_t<
                            std::is_same<typename T::value_type,
                                        std::pair<const typename T::key_type,
                                                  typename T::mapped_type>
                            >::value>
        > : std::true_type {};
    }
    
    template <typename T>
    constexpr bool is_map = detail::IsMap<T>::value;
    
    namespace { template <bool> struct MapTagImpl {}; }
    using MapTag    = MapTagImpl<true>;
    using NonMapTag = MapTagImpl<false>;
    
    template <typename T>
    using MapTagType = MapTagImpl<is_map<T>>;