For known reasons (elaborated e.g. here) one shouldn't customize std
namespace. This cause one is discouraged to use minimalistic approach of redefining hasher for (not only) user-defined types by specialization std::hash
which, as every modification of std
namespace, may bring undefined behaviour. As such only valid way to use unordered collection with custom key is by creating his own hash helper struct and passing it to the collection. However passing it every time when accessing collection type is in my opinion somewhat inconvenient. On the other hand c++11 comes with a syntax of templated using to perform templated type aliasing. I haven't seen any answer on stackoverflow about unordered collections encouraging to use that approach. Are there any downsides for adopting templated using to create custom default values of templated parameters? Is there any better pattern covering general problem of introducing new/changing existing default values of templated parameters of classes from std
namespace? Example applying hash of 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;
}