检查类型是否为地图

Checking if a type is a map

本文关键字:地图 是否 类型 检查      更新时间:2023-10-16

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

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

此标记调度的安全、清洁的方法是什么?

现有的答案测试了std::map非常具体的属性,要么它恰恰是std::map的专业化(这对于与std::map具有相同接口的std::unordered_map或非标准类型是错误的),要么测试其value_type完全std::pair<const key_type, mapped_type>(对于multimapunordered_map都是如此, 但对于具有类似接口的非标准类型为 false)。

这仅测试它是否提供key_typemapped_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_typefalse_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
}

这对我有用,但不是 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
}

这是我想到的:

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