/** @addtogroup Containers * @{ */ /** @file unordered_map.hh * * @brief Extensions for std::unordered_map. * * This file provides extensions for the standard std::unordered_map container. * * @copyright See COPYING.md in the project tree for further information. */ #ifndef OSTD_UNORDERED_MAP_HH #define OSTD_UNORDERED_MAP_HH #include #include #include #include "ostd/range.hh" namespace ostd { /** @addtogroup Containers * @{ */ 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; } /** @brief Creates an unordered map using a range. * * The range's value type must be either an std::pair or an std::tuple * with 2 elements, additionally the key and value types must be constructible * using the tuple or pair's first and second element respectively. * * You need to manually specify the key and value types for this overload. * * @param[in] range The range. * @param[in] bcount The initial bucket count. * @param[in] hash The hash function. * @param[in] kequal The key equality comparison function. * @param[in] alloc The allocator. * * @tparam K The key type. * @tparam V The value type. */ template< typename K, typename T, typename H = std::hash, typename E = std::equal_to, typename A = std::allocator>, typename R > inline std::unordered_map make_unordered_map( R range, std::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, range_value_t>; using AV = std::tuple_element_t<1, range_value_t>; static_assert( std::is_constructible_v && std::is_constructible_v, "incompatible range element type" ); std::unordered_map ret{bcount, hash, kequal, alloc}; using C = range_category_t; if constexpr(std::is_convertible_v) { /* at least try to preallocate here... */ ret.reserve(range.size()); } for (; !range.empty(); range.pop_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 */ range_value_t v{range.front()}; ret.emplace(std::move(std::get<0>(v)), std::move(std::get<1>(v))); } } return ret; } /** @brief Creates an unordered map using a range. * * Calls into make_unordered_map() using the range value type's first and * second element types as key and value respectively. */ template< typename R, typename H = std::hash::first_type>, typename E = std::equal_to::first_type>, typename A = std::allocator>, std::tuple_element_t<1, range_value_t> >> > inline std::unordered_map< std::tuple_element_t<0, range_value_t>, std::tuple_element_t<1, range_value_t>, H, E, A > make_unordered_map( R &&range, std::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" ); return make_unordered_map< std::tuple_element_t<0, range_value_t>, std::tuple_element_t<1, range_value_t>, H, E, A >(std::forward(range), bcount, hash, kequal, alloc); } /** @} */ } /* namespace ostd */ #endif /** @} */