【问题标题】:Checking if a type is a map检查类型是否为地图
【发布时间】:2016-05-19 12:31:45
【问题描述】:

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

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

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

【问题讨论】:

  • 检查是否嵌套了key_type和mapped_type类型?

标签: c++ dictionary c++14 typetraits


【解决方案1】:

现有答案测试std::map 的非常具体的属性,或者它恰好是std::map 的特化(这对于std::unordered_map 或具有与std::map 相同接口的非标准类型是错误的) , 或者测试它的value_type 是否正好是std::pair&lt;const key_type, mapped_type&gt;(这对于multimap 和unordered_map 是正确的,但对于具有类似接口的非标准类型是错误的)。

这里只测试它提供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
}

【讨论】:

  • is_mappish_impl 让我发笑。 :)
  • 虽然,这个方案也只有在非标准地图实现标准关联容器接口的情况下才有效,这个也有可能坏掉。
  • @DavidHaim,显然是的。如果该类型没有实现特定的接口,那么您可能不想分派到依赖该接口的模板函数。可以轻松定制该特征,以检查地图界面的任何部分与与地图一起使用的 OP 的 foo 相关,无论“地图”的任何定义与该功能相关。
  • 这可以用在变量var上吗,以这样的方式(通常将is_map命名为函数)is_map(var)返回bool?
  • @sancho.sReinstateMonica 是的,当然,只需编写一个函数模板,返回函数参数类型的is_mappish trait 的值。
【解决方案2】:

这对我有用,但没有经过 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
}

【讨论】:

  • 值得注意的是,这只适用于std::map,不适用于其他“地图”类型(例如std::unordered_map)。我可能应该更具体地回答这个问题。
  • @Daniel 然后在需要时为 unordered_map 添加一个特化
  • @Daniel 我假设您谈论的是标准常规地图。这可以很容易地转换为支持 std::unordered_map ,随后支持 isAnyMap。尽管如此,我怀疑有人会在同一个项目中使用超过 1-2 种类型的地图..
  • 我认为这会破坏带有自定义分配器和比较器的地图。通过添加另外两个模板参数可以轻松修复它。
  • 这可以用在变量var上吗,以这样的方式(通常将is_map命名为函数)is_map(var)返回bool?
【解决方案3】:

这是我想出的:

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

【讨论】:

  • 从true_type 或false_type 派生比定义static constexpr bool value 更简单。此外,您的特性对于多地图也是如此,这可能是不可取的(它们没有相同的界面)。
  • 这可以用在变量var上吗,以这样的方式(通常将is_map命名为函数)is_map(var)返回bool?
猜你喜欢
  • 2011-09-26
  • 2020-08-19
  • 2012-11-15
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-24
  • 1970-01-01
相关资源
最近更新 更多