1

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;
}
Community
  • 1
  • 1
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • @Downvoter help me improve my question and tell me what's wrong with it... – W.F. Jun 03 '16 at 12:41
  • btw, you **can** specialize `std::hash` – bolov Jun 04 '16 at 13:32
  • @bolov I've changed cannot -> shouldn't and forbidden -> discouraged as specification says it may cause undefined behaviour – W.F. Jun 04 '16 at 13:48
  • you cannot (it's illegal) to extend namespace `std`. One exception is specializing `std::hash`: it is allowed and encouraged. – bolov Jun 04 '16 at 14:09
  • see this: http://stackoverflow.com/questions/23339298/stdhash-template-partial-specialization – bolov Jun 04 '16 at 14:12
  • and this: http://en.cppreference.com/w/cpp/language/extending_std – bolov Jun 04 '16 at 14:13
  • and this: http://stackoverflow.com/questions/8157937/how-to-specialize-stdhashkeyoperator-for-user-defined-type-in-unordered – bolov Jun 04 '16 at 14:14
  • @bolov thanks! I was sure that there is no exception from the rule! – W.F. Jun 04 '16 at 14:57
  • @bolov Does this mean one is allowed to partially specialize std::hash for the std::tuple that will in a future specialization contain his type as follows: `template struct hash> {/*...*/}`? – W.F. Jun 04 '16 at 15:06
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/113812/discussion-between-w-f-and-bolov). – W.F. Jun 04 '16 at 15:16

1 Answers1

1

I see 2 drawbacks:

First drawback is disputable - I'd just use "raw" std types, for me such code is easier to read/understand:

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

Second is that some newer version of C++ standard might have other defaults - so your projects will be in trouble of not being confirmed to new C++ standard, like - I can easy imagine that they change default comparator to std::equal_to<> for all types and (what is much less probable) allocator parameter type from pair to tuple - deprecating pair - or even to std::allocator<> expressing that it is not really important this parameter type to allocator...

Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • Good point with the forward compatibility! But about first drawback - creating alias for each possible `std::unordered_map` isn't quite scalable especially when it comes to variadics like `std::tuple`... – W.F. Jun 03 '16 at 15:27
  • what to you think if I would switch default parameter values to: `class KeyEqual = std::unordered_map::key_equal` and `class Allocator = std::unordered_map::allocator_type` ? Do you think it would break less the possible future standard? – W.F. Jun 03 '16 at 18:44
  • Yes, that would be better. However, maybe I should say at the beginning of my post - these are very minor drawbacks - your original code is not bad, it is acceptable... – PiotrNycz Jun 05 '16 at 21:23