diff --git a/ostd/unordered_map.hh b/ostd/unordered_map.hh index 15eb971..91928d6 100644 --- a/ostd/unordered_map.hh +++ b/ostd/unordered_map.hh @@ -33,6 +33,19 @@ struct ranged_traits const> { } }; +namespace detail { + template + std::integral_constant< + bool, std::tuple_size::value == 2 + > tuple2_like_test(typename std::tuple_size::type *); + + template + std::false_type tuple2_like_test(...); + + template + constexpr bool is_2tuple_like = decltype(tuple2_like_test(0))::value; +} + template< typename K, typename T, typename H = std::hash, typename E = std::equal_to, @@ -42,14 +55,39 @@ inline std::unordered_map make_unordered_map( R range, size_t bcount = 1, H const &hash = H{}, E const &kequal = E{}, A const &alloc = A{} ) { + static_assert( + detail::is_2tuple_like>, + "the range element must be a pair/2-tuple" + ); + + using MP = std::pair; + using AK = std::tuple_element_t<0, RangeValue>; + using AV = std::tuple_element_t<1, RangeValue>; + + static_assert( + std::is_constructible_v && std::is_constructible_v, + "incompatible range element type" + ); + std::unordered_map ret{bcount, hash, kequal, alloc}; + using C = RangeCategory; if constexpr(std::is_convertible_v) { /* at least try to preallocate here... */ ret.reserve(range.size()); } + for (; !range.empty(); range.pop_front()) { - ret.emplace(range.front()); + if constexpr(std::is_constructible_v>) { + ret.emplace(range.front()); + } else { + /* store a temporary to prevent calling front() twice; however, + * for values that can be used to construct the pair directly + * we can just do the above + */ + RangeValue v{range.front()}; + ret.emplace(std::move(std::get<0>(v)), std::move(std::get<1>(v))); + } } return ret; } @@ -59,19 +97,24 @@ template< typename H = std::hash::first_type>, typename E = std::equal_to::first_type>, typename A = std::allocator::first_type, typename RangeValue::second_type + std::tuple_element_t<0, RangeValue>, + std::tuple_element_t<1, RangeValue> >> > inline std::unordered_map< - typename RangeValue::first_type, - typename RangeValue::second_type, H, E, A + std::tuple_element_t<0, RangeValue>, + std::tuple_element_t<1, RangeValue>, H, E, A > make_unordered_map( R &&range, size_t bcount = 1, H const &hash = H{}, E const &kequal = E{}, A const &alloc = A{} ) { + static_assert( + detail::is_2tuple_like>, + "the range elements must be pairs/2-tuples" + ); return make_unordered_map< - typename RangeValue::first_type, - typename RangeValue::second_type, H, E, A + std::tuple_element_t<0, RangeValue>, + std::tuple_element_t<1, RangeValue>, H, E, A >(std::forward(range), bcount, hash, kequal, alloc); }