c++ - 使用模板化是否为带有自定义键的 unordered_map/set 制作了一个好的模式

标签 c++ templates c++11 unordered-map unordered-set

<分区>

出于已知原因(详细说明,例如 here),不应自定义 std 命名空间。这导致人们不鼓励使用通过专门化 std::hash 为(不仅是)用户定义类型重新定义散列器的简约方法,随着 std 命名空间的每次修改,可能会带来未定义的行为。因此,使用带有自定义键的无序集合的唯一有效方法是创建他自己的哈希助手结构并将其传递给集合。然而,在我看来,每次访问集合类型时都传递它有点不方便。另一方面,c++11 带有模板化语法,用于执行模板化类型别名。我还没有在 stackoverflow 上看到任何关于鼓励使用该方法的无序集合的答案。采用模板化使用来创建模板化参数的自定义默认值是否有任何缺点?是否有更好的模式涵盖从 std 命名空间引入新的/更改类的模板化参数的现有默认值的一般问题?应用 std::tuple 散列的示例:

#include <tuple>
#include <utility>
#include <unordered_map>

template <class T>
struct my_hash: std::hash<T> { };

template <
    class Key,
    class T,
    class Hash = my_hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> > >
using my_unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator>;

template <class...>
struct integral_sequence { };

template <int Start, int End, class = integral_sequence<>>
struct make_integral_sequence_impl;

template <int Start, int End, class... Integrals>
struct make_integral_sequence_impl<Start, End, integral_sequence<Integrals...> >: make_integral_sequence_impl<Start+1, End, integral_sequence<Integrals..., std::integral_constant<int, Start>>> { };

template <int StartAndBegin, class... Integrals>
struct make_integral_sequence_impl<StartAndBegin, StartAndBegin, integral_sequence<Integrals...>>{
   using type = integral_sequence<Integrals..., std::integral_constant<int, StartAndBegin>>;
};

template <int Start, int End>
using make_integral_sequence = typename make_integral_sequence_impl<Start, End>::type;

template <class Tuple>
struct sizer;

template <class... Args>
struct sizer<std::tuple<Args...>> {
   static constexpr size_t value = sizeof...(Args);
};

template <class Tuple, class Indices = make_integral_sequence<1, sizer<Tuple>::value - 1 > >
struct tuple_tail_impl;

template <class Head, class... Tail, class... Indices>
struct tuple_tail_impl<std::tuple<Head, Tail...>, integral_sequence<Indices...>> {
    using type = std::tuple<Tail...>;
    std::tuple<Head, Tail...> tuple;
    std::tuple<Tail...> get() {
       std::tuple<Tail...> result { std::get<Indices::value>(tuple)... };
       return result;
    }
};

template <class Tuple>
typename tuple_tail_impl<Tuple>::type tuple_tail(const Tuple &t) {
   tuple_tail_impl<Tuple> tti { t };
   return tti.get();
}

template <class First, class... Other>
struct my_hash<std::tuple<First, Other...>> {
   std::size_t operator()(const std::tuple<First, Other...>& val) const {
      return 805306457 * my_hash<std::tuple<Other...>>()(tuple_tail(val)) + my_hash<First>()(std::get<0>(val));
   }
};

template <class First>
struct my_hash<std::tuple<First>> {
   std::size_t operator()(const std::tuple<First>& val) const {
      return my_hash<First>()(std::get<0>(val));
   }
};


int main() {
   my_unordered_map<std::tuple<int, int>, int> mum;
   mum[std::make_tuple(1, 2)] = 10;
   mum[std::make_tuple(2, 3)] = 20;
}

最佳答案

我看到了 2 个缺点:

第一个 缺点是有争议的——我只使用“原始”std 类型,对我来说这样的代码更容易阅读/理解:

using mkey = std::tuple<int, int>;
std::unordered_map<mkey, int, my_hash<my_key>> mum;

其次是某些较新版本的 C++ 标准可能具有其他默认值 - 因此您的项目将遇到未确认新 C++ 标准的麻烦,例如 - 我可以很容易地想象它们会更改默认值比较 std::equal_to<> 对于来自 pair 的所有类型和(不太可能的)分配器参数类型至 tuple - 弃用对 - 甚至是 std::allocator<>表示这个参数类型对分配器来说并不重要......

关于c++ - 使用模板化是否为带有自定义键的 unordered_map/set 制作了一个好的模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37573473/

相关文章:

c++ - 根据用户输入创建特定类型的对象

c++ - 可变参数模板必须具有非空参数包的可调用特化才能格式正确吗?

c++ - VS C++ 2012(13) 无法将重载函数转换为 std::function

php - 自定义 PHP 扩展不适用于 5.3

c++ - 如何将 cpp 文件添加到 Visual Studio 2008?

c++ - 如何让列表可以隐式转换为我的类对象?

c++ - 乘法运算符重载

c++ - 在可变数量的标记碱基上进行模板化

c++ - 提取模板类默认参数

c++ - std::vector resize(0) 或 clear() - 但保持其容量