diff --git a/include/sg14/flat_map.h b/include/sg14/flat_map.h index ff1737e..2d5e29e 100644 --- a/include/sg14/flat_map.h +++ b/include/sg14/flat_map.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #if __cplusplus >= 202002L @@ -50,12 +51,6 @@ namespace sg14 { namespace flatmap_detail { - template struct qualifies_as_range : std::false_type {}; - template struct qualifies_as_range()() ), void(), - std::end( std::declval()() ), void() - )> : std::true_type {}; - template using is_random_access_iterator = std::is_convertible< typename std::iterator_traits::iterator_category, @@ -93,12 +88,6 @@ namespace flatmap_detail { return make_obj_using_allocator_(priority_tag<3>(), alloc, static_cast(args)...); } - template - using cont_key_type = typename std::remove_const::type; - - template - using cont_mapped_type = typename Container::value_type::second_type; - template using iter_key_type = typename std::remove_const::value_type::first_type>::type; @@ -176,7 +165,7 @@ namespace flatmap_detail { } template - It unique_helper(It first, It last, It2 mapped, Compare& compare) { + It unique_helper(It first, It last, It2 mapped, const Compare& compare) { It dfirst = first; It2 dmapped = mapped; while (first != last) { @@ -199,6 +188,20 @@ namespace flatmap_detail { return dfirst; } + template + struct InvariantRestoringGuard { + FS *self_; + explicit InvariantRestoringGuard(FS *self) : self_(self) {} + void complete() { + self_ = nullptr; + } + ~InvariantRestoringGuard() { + if (self_ != nullptr) { + self_->clear(); + } + } + }; + template class iter; template iter make_iterator(K, V); @@ -215,7 +218,7 @@ namespace flatmap_detail { class iter { public: using difference_type = ptrdiff_t; - using value_type = std::pair::value_type, typename std::iterator_traits::value_type>; + using value_type = std::pair::value_type, typename std::iterator_traits::value_type>; using reference = std::pair::reference, typename std::iterator_traits::reference>; using pointer = arrow_proxy; using iterator_category = std::random_access_iterator_tag; @@ -241,6 +244,18 @@ namespace flatmap_detail { return arrow_proxy{reference{*kit_, *vit_}}; } + friend auto iter_move(const iter& it) { +#if __cpp_lib_ranges >= 201911L + using K = decltype(std::ranges::iter_move(it.kit_)); + using V = decltype(std::ranges::iter_move(it.vit_)); + return std::pair(std::ranges::iter_move(it.kit_), std::ranges::iter_move(it.vit_)); +#else + using K = decltype(std::move(*it.kit_)); + using V = decltype(std::move(*it.vit_)); + return std::pair(std::move(*it.kit_), std::move(*it.vit_)); +#endif + } + iter& operator++() { ++kit_; ++vit_; return *this; } iter& operator--() { --kit_; --vit_; return *this; } iter operator++(int) { iter result(*this); ++*this; return result; } @@ -331,17 +346,13 @@ class flat_map { using key_container_type = KeyContainer; using mapped_container_type = MappedContainer; - class value_compare { - friend class flat_map; - protected: - // TODO: this should be private - Compare comp; - // TODO: this constructor should be explicit - value_compare(Compare c): comp(c) {} - public: + struct value_compare { + explicit value_compare(Compare c): comp_(std::move(c)) {} bool operator()(const_reference x, const_reference y) const { - return comp(x.first, y.first); + return comp_(x.first, y.first); } + private: + Compare comp_; }; struct containers { @@ -354,77 +365,6 @@ class flat_map { flat_map() = default; - flat_map(KeyContainer keys, MappedContainer values) - : keys_(static_cast(keys)), values_(static_cast(values)) - { - this->sort_and_unique_impl(); - } - - template::value && std::uses_allocator::value, int>::type = 0> - flat_map(KeyContainer keys, MappedContainer values, const Alloc& a) - : keys_(flatmap_detail::make_obj_using_allocator(a, std::move(keys))), - values_(flatmap_detail::make_obj_using_allocator(a, std::move(values))) - { - this->sort_and_unique_impl(); - } - - template::value, int>::type = 0> - explicit flat_map(const Container& cont) - : flat_map(std::begin(cont), std::end(cont), Compare()) {} - - template::value, int>::type = 0> - explicit flat_map(const Container& cont, const Compare& comp) - : flat_map(std::begin(cont), std::end(cont), comp) {} - - template::value>::type, - class = typename std::enable_if::value>::type, - class = typename std::enable_if::value>::type> - flat_map(const Container& cont, const Alloc& a) - : flat_map(std::begin(cont), std::end(cont), Compare(), a) {} - - template::value>::type, - class = typename std::enable_if::value>::type, - class = typename std::enable_if::value>::type> - flat_map(const Container& cont, const Compare& comp, const Alloc& a) - : flat_map(std::begin(cont), std::end(cont), comp, a) {} - - flat_map(sorted_unique_t, KeyContainer keys, MappedContainer values) - : keys_(static_cast(keys)), values_(static_cast(values)) {} - - template::value && std::uses_allocator::value, int>::type = 0> - flat_map(sorted_unique_t s, KeyContainer keys, MappedContainer values, const Alloc& a) - : flat_map(s, KeyContainer(static_cast(keys), a), MappedContainer(static_cast(values), a)) {} - - template::value>::type> - flat_map(sorted_unique_t s, const Container& cont) - : flat_map(s, std::begin(cont), std::end(cont), Compare()) {} - - template::value>::type> - flat_map(sorted_unique_t s, const Container& cont, const Compare& comp) - : flat_map(s, std::begin(cont), std::end(cont), comp) {} - - template::value>::type, - class = typename std::enable_if::value>::type, - class = typename std::enable_if::value>::type> - flat_map(sorted_unique_t s, const Container& cont, const Alloc& a) - : flat_map(s, std::begin(cont), std::end(cont), Compare(), a) {} - - template::value>::type, - class = typename std::enable_if::value>::type, - class = typename std::enable_if::value>::type> - flat_map(sorted_unique_t s, const Container& cont, const Compare& comp, const Alloc& a) - : flat_map(s, std::begin(cont), std::end(cont), comp, a) {} - explicit flat_map(const Compare& comp) : compare_(comp) {} @@ -440,18 +380,52 @@ class flat_map { explicit flat_map(const Alloc& a) : flat_map(Compare(), a) {} - // TODO: shouldn't InputIterator be constrained to point to something with "first" and "second" members? +#if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L + template + requires std::convertible_to, value_type> + flat_map(std::from_range_t, R&& rg) + { + this->insert_range(static_cast(rg)); + } + + template + requires std::convertible_to, value_type> && + std::uses_allocator_v && + std::uses_allocator_v + flat_map(std::from_range_t, R&& rg, const Alloc& a) + : keys_(flatmap_detail::make_obj_using_allocator(a)), + values_(flatmap_detail::make_obj_using_allocator(a)) + { + this->insert_range(static_cast(rg)); + } + + template + requires std::convertible_to, value_type> + flat_map(std::from_range_t, R&& rg, const Compare& comp) + : compare_(comp) + { + this->insert_range(static_cast(rg)); + } + + template + requires std::convertible_to, value_type> && + std::uses_allocator_v && + std::uses_allocator_v + flat_map(std::from_range_t, R&& rg, const Compare& comp, const Alloc& a) + : keys_(flatmap_detail::make_obj_using_allocator(a)), + values_(flatmap_detail::make_obj_using_allocator(a)), + compare_(comp) + { + this->insert_range(static_cast(rg)); + } +#endif // __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L + template::value>::type> flat_map(InputIterator first, InputIterator last, const Compare& comp = Compare()) : compare_(comp) { - for (; first != last; ++first) { - keys_.insert(keys_.end(), first->first); - // TODO: we must make this exception-safe if the container insert throws - values_.insert(values_.end(), first->second); - } - this->sort_and_unique_impl(); + this->insert(first, last); } template(a)), compare_(comp) { - for (; first != last; ++first) { - keys_.insert(keys_.end(), first->first); - values_.insert(values_.end(), first->second); - } - this->sort_and_unique_impl(); + this->insert(first, last); } templatefirst); - values_.insert(values_.end(), first->second); - } + this->insert(sorted_unique, first, last); } template(a)), compare_(comp) { - for (; first != last; ++first) { - keys_.insert(keys_.end(), first->first); - values_.insert(values_.end(), first->second); - } + this->insert(sorted_unique, first, last); } template::value && std::uses_allocator::value, int>::type = 0> flat_map(flat_map&& m, const Alloc& a) @@ -525,7 +488,10 @@ class flat_map { values_(flatmap_detail::make_obj_using_allocator(a, m.values_)), compare_(m.compare_) {} - flat_map(std::initializer_list il, const Compare& comp = Compare()) + flat_map(std::initializer_list il) + : flat_map(il.begin(), il.end()) {} + + flat_map(std::initializer_list il, const Compare& comp) : flat_map(il.begin(), il.end(), comp) {} template::value && std::uses_allocator::value, int>::type = 0> flat_map(std::initializer_list il, const Alloc& a) - : flat_map(il.begin(), il.end(), a) {} + : flat_map(il.begin(), il.end(), Compare(), a) {} flat_map(sorted_unique_t s, std::initializer_list il, const Compare& comp = Compare()) : flat_map(s, il.begin(), il.end(), comp) {} @@ -555,7 +521,7 @@ class flat_map { flat_map& operator=(std::initializer_list il) { this->clear(); - this->insert(il); + this->insert(il.begin(), il.end()); return *this; } @@ -693,39 +659,76 @@ class flat_map { this->insert(s, il.begin(), il.end()); } - containers extract() && { - try { - containers result{ - static_cast(keys_), - static_cast(values_) - }; - this->clear(); - return result; - } catch (...) { - this->clear(); - throw; +#if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L + template + requires std::convertible_to, value_type> + void insert_range(R&& rg) { + flatmap_detail::InvariantRestoringGuard guard(this); + size_type oldsize = keys_.size(); + for (value_type e : rg) { + keys_.emplace_back(std::move(e.first)); + values_.emplace_back(std::move(e.second)); } + if (keys_.size() != oldsize) { + auto begin = flatmap_detail::make_iterator(keys_.begin(), values_.begin()); + auto mid = begin + oldsize; + auto end = flatmap_detail::make_iterator(keys_.end(), values_.end()); + std::ranges::sort(mid, end, value_comp()); + std::ranges::inplace_merge(begin, mid, end, value_comp()); + auto kit = flatmap_detail::unique_helper(keys_.begin(), keys_.end(), values_.begin(), compare_); + auto vit = values_.begin() + (kit - keys_.begin()); + keys_.erase(kit, keys_.end()); + values_.erase(vit, values_.end()); + } + guard.complete(); } - void replace(KeyContainer keys, MappedContainer values) { - try { - keys_ = static_cast(keys); - values_ = static_cast(values); - this->sort_and_unique_impl(); - } catch (...) { - this->clear(); - throw; + template + requires std::convertible_to, value_type> + void insert_range(sg14::sorted_unique_t, R&& rg) { + flatmap_detail::InvariantRestoringGuard guard(this); + size_type oldsize = keys_.size(); + for (value_type e : rg) { + keys_.emplace_back(std::move(e.first)); + values_.emplace_back(std::move(e.second)); + } + if (keys_.size() != oldsize) { + auto begin = flatmap_detail::make_iterator(keys_.begin(), values_.begin()); + auto mid = begin + oldsize; + auto end = flatmap_detail::make_iterator(keys_.end(), values_.end()); + std::ranges::inplace_merge(begin, mid, end, compare_); + auto kit = flatmap_detail::unique_helper(keys_.begin(), keys_.end(), values_.begin(), compare_); + auto vit = values_.begin() + (kit - keys_.begin()); + keys_.erase(kit, keys_.end()); + values_.erase(vit, values_.end()); } + guard.complete(); + } +#endif + + containers extract() && { + flatmap_detail::InvariantRestoringGuard guard(this); + containers result{ + static_cast(keys_), + static_cast(values_) + }; + // Deliberately do not complete the guard. + return result; + } + + void replace(KeyContainer keys, MappedContainer values) { + flatmap_detail::InvariantRestoringGuard guard(this); + keys_ = static_cast(keys); + values_ = static_cast(values); + this->sort_and_unique_impl(); + guard.complete(); } void replace(sorted_unique_t, KeyContainer keys, MappedContainer values) { - try { - keys_ = static_cast(keys); - values_ = static_cast(values); - } catch (...) { - this->clear(); - throw; - } + flatmap_detail::InvariantRestoringGuard guard(this); + keys_ = static_cast(keys); + values_ = static_cast(values); + guard.complete(); } template @@ -1118,120 +1121,31 @@ class flat_map { #if defined(__cpp_deduction_guides) -// TODO: this deduction guide should maybe be constrained by qualifies_as_range -template::value>> -flat_map(Container) - -> flat_map, flatmap_detail::cont_mapped_type>; - -template::value && !flatmap_detail::qualifies_as_allocator::value>> -flat_map(KeyContainer, MappedContainer) - -> flat_map, - KeyContainer, MappedContainer>; - -// TODO: all these deduction guides that ignore the Allocator parameter are wrong, but especially this one -template::value && flatmap_detail::qualifies_as_allocator::value && std::uses_allocator::value>> -flat_map(Container, Allocator, int=0/*to please MSVC*/) - -> flat_map, flatmap_detail::cont_mapped_type>; - -template::value && !flatmap_detail::qualifies_as_allocator::value && flatmap_detail::qualifies_as_allocator::value && std::uses_allocator::value && std::uses_allocator::value>> -flat_map(KeyContainer, MappedContainer, Allocator) - -> flat_map, - KeyContainer, MappedContainer>; - -template::value>> -flat_map(sorted_unique_t, Container) - -> flat_map, flatmap_detail::cont_mapped_type>; - -template::value && !flatmap_detail::qualifies_as_allocator::value>> -flat_map(sorted_unique_t, KeyContainer, MappedContainer) - -> flat_map, - KeyContainer, MappedContainer>; - -template::value && flatmap_detail::qualifies_as_allocator::value && std::uses_allocator::value>> -flat_map(sorted_unique_t, Container, Allocator, int=0/*to please MSVC*/) - -> flat_map, flatmap_detail::cont_mapped_type>; - -template::value && !flatmap_detail::qualifies_as_allocator::value && flatmap_detail::qualifies_as_allocator::value && std::uses_allocator::value && std::uses_allocator::value>> -flat_map(sorted_unique_t, KeyContainer, MappedContainer, Allocator) - -> flat_map, - KeyContainer, MappedContainer>; - -template>, +template, + class T = flatmap_detail::iter_mapped_type, + class Compare = std::less, class = std::enable_if_t::value && !flatmap_detail::qualifies_as_allocator::value>> flat_map(InputIterator, InputIterator, Compare = Compare()) - -> flat_map, flatmap_detail::iter_mapped_type, Compare>; - -template::value && !flatmap_detail::qualifies_as_allocator::value && flatmap_detail::qualifies_as_allocator::value>> -flat_map(InputIterator, InputIterator, Compare, Allocator) - -> flat_map, flatmap_detail::iter_mapped_type, Compare>; - -template::value && flatmap_detail::qualifies_as_allocator::value>> -flat_map(InputIterator, InputIterator, Allocator, int=0/*to please MSVC*/) - -> flat_map, flatmap_detail::iter_mapped_type>; + -> flat_map; -template>, +template, + class T = flatmap_detail::iter_mapped_type, + class Compare = std::less, class = std::enable_if_t::value && !flatmap_detail::qualifies_as_allocator::value>> flat_map(sorted_unique_t, InputIterator, InputIterator, Compare = Compare()) - -> flat_map, flatmap_detail::iter_mapped_type, Compare>; - -template::value && !flatmap_detail::qualifies_as_allocator::value && flatmap_detail::qualifies_as_allocator::value>> -flat_map(sorted_unique_t, InputIterator, InputIterator, Compare, Allocator) - -> flat_map, flatmap_detail::iter_mapped_type, Compare>; - -template::value && flatmap_detail::qualifies_as_allocator::value>> -flat_map(sorted_unique_t, InputIterator, InputIterator, Allocator, int=0/*to please MSVC*/) - -> flat_map, flatmap_detail::iter_mapped_type>; - -template, - class = std::enable_if_t::value>> -flat_map(std::initializer_list>, Compare = Compare()) - -> flat_map; - -template::value && flatmap_detail::qualifies_as_allocator::value>> -flat_map(std::initializer_list>, Compare, Allocator) - -> flat_map; - -template::value>> -flat_map(std::initializer_list>, Allocator, int=0/*to please MSVC*/) - -> flat_map; - -template, - class = std::enable_if_t::value>> -flat_map(sorted_unique_t, std::initializer_list>, Compare = Compare()) -> flat_map; -template::value && flatmap_detail::qualifies_as_allocator::value>> -flat_map(sorted_unique_t, std::initializer_list>, Compare, Allocator) +#if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L +template>, + class T = flatmap_detail::iter_mapped_type>, + class Compare = std::less> +flat_map(std::from_range_t, R&&, Compare = Compare()) -> flat_map; +#endif // __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L -template::value>> -flat_map(sorted_unique_t, std::initializer_list>, Allocator, int=0/*to please MSVC*/) - -> flat_map; - -#endif +#endif // __cpp_deduction_guides } // namespace sg14 diff --git a/test/flat_map_test.cpp b/test/flat_map_test.cpp index 47d1ac3..3d33761 100644 --- a/test/flat_map_test.cpp +++ b/test/flat_map_test.cpp @@ -15,6 +15,10 @@ #include #endif +#if __cplusplus >= 202002L +#include +#endif + template struct flat_mapt : testing::Test {}; using flat_mapt_types = testing::Types< @@ -174,6 +178,23 @@ TEST(flat_map, SortedUniqueConstruction) #endif } +TEST(flat_map, MoveOnly) +{ + using T = std::unique_ptr; + sg14::flat_map fm; +#if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L && __cpp_lib_ranges_as_rvalue >= 202207L + std::vector> pairs; + fm = sg14::flat_map(std::from_range, pairs | std::views::as_rvalue); + fm = sg14::flat_map(std::from_range, pairs | std::views::as_rvalue, std::less()); +#endif + std::vector keys; + std::vector values; + fm.replace(keys, std::move(values)); + fm.replace(std::move(keys), std::move(values)); + fm.replace(sg14::sorted_unique, keys, std::move(values)); + fm.replace(sg14::sorted_unique, std::move(keys), std::move(values)); +} + TEST(flat_map, TryEmplace) { sg14::flat_map fm; @@ -293,128 +314,31 @@ TEST(flat_map, DeductionGuides) { using sg14::flat_map; #if defined(__cpp_deduction_guides) +#if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L if (true) { - // flat_map(Container) - std::vector> v; - flat_map fm1(v); - static_assert(std::is_same_v>); - flat_map fm2 = flat_map(std::deque>()); - static_assert(std::is_same_v>); - std::list> lst; - flat_map fm3(lst); - static_assert(std::is_same_v>); -#if __cpp_lib_memory_resource >= 201603 - std::pmr::vector> pv; - flat_map fm4(pv); - static_assert(std::is_same_v>); -#endif - std::initializer_list> il = {{1,"c"}, {5,"b"}, {3,"a"}}; - flat_map fm5(il); - static_assert(std::is_same_v>); - EXPECT_EQ(fm5.size(), 3); - EXPECT_EQ(fm5, decltype(fm5)(sg14::sorted_unique, {{1,"c"}, {3,"a"}, {5,"b"}})); - } - if (true) { - // flat_map(KeyContainer, MappedContainer) - std::vector vi {2,1}; - std::vector vs {"a","b"}; - flat_map fm1(vi, vs); - static_assert(std::is_same_v>); - EXPECT_EQ(fm1, (flat_map(sg14::sorted_unique, {{1,"b"}, {2,"a"}}))); - flat_map fm2(std::move(vs), std::move(vi)); - static_assert(std::is_same_v>); - EXPECT_EQ(fm2, (flat_map(sg14::sorted_unique, {{"a",2}, {"b",1}}))); - } - if (true) { - // flat_map(Container, Allocator) - std::vector> v; - flat_map fm1(v, std::allocator()); - static_assert(std::is_same_v>); -#if __cpp_lib_memory_resource >= 201603 - std::pmr::vector> pv; - // TODO: neither of these lines compiles, and it's unclear what is INTENDED to happen - // flat_map fm2(pv, std::allocator()); - // flat_map fm2(pv, std::pmr::polymorphic_allocator()); -#endif - } - if (true) { - // flat_map(KeyContainer, MappedContainer, Allocator) - std::vector vi {2,1}; - std::vector vs {"a","b"}; - flat_map fm1(vi, vs, std::allocator()); - static_assert(std::is_same_v>); - EXPECT_EQ(fm1, (decltype(fm1)(sg14::sorted_unique, {{1,"b"}, {2,"a"}}))); -#if __cpp_lib_memory_resource >= 201603 - std::pmr::vector pvi {2,1}; - std::pmr::vector pvs {"a","b"}; - flat_map fm2(pvi, pvs, std::pmr::polymorphic_allocator()); - static_assert(std::is_same_v, std::pmr::vector, std::pmr::vector>>); - EXPECT_EQ(fm2, (decltype(fm2)(sg14::sorted_unique, {{1,"b"}, {2,"a"}}))); -#endif - } - if (true) { - // flat_map(sorted_unique_t, Container) + // flat_map(std::from_range_t, R&&) std::vector> v; - flat_map fm1(sg14::sorted_unique, v); + flat_map fm1(std::from_range, v); static_assert(std::is_same_v>); - flat_map fm2 = flat_map(sg14::sorted_unique, std::deque>()); + flat_map fm2 = flat_map(std::from_range, std::deque>()); static_assert(std::is_same_v>); std::list> lst; - flat_map fm3(sg14::sorted_unique, lst); + flat_map fm3(std::from_range, lst); static_assert(std::is_same_v>); #if __cpp_lib_memory_resource >= 201603 std::pmr::vector> pv; - flat_map fm4(sg14::sorted_unique, pv); + flat_map fm4(std::from_range, pv); static_assert(std::is_same_v>); #endif - std::initializer_list> il = {{1,"c"}, {3,"b"}, {5,"a"}}; - flat_map fm5(sg14::sorted_unique, il); - static_assert(std::is_same_v>); - EXPECT_EQ(fm5, (decltype(fm5)(sg14::sorted_unique, {{1,"c"}, {3,"b"}, {5,"a"}}))); - } - if (true) { - // flat_map(sorted_unique_t, KeyContainer, MappedContainer) - std::vector vi {1,2}; - std::vector vs {"a","b"}; - flat_map fm1(sg14::sorted_unique, vi, vs); - static_assert(std::is_same_v>); - EXPECT_EQ(fm1, decltype(fm1)(sg14::sorted_unique, {{1,"a"}, {2,"b"}})); - flat_map fm2(sg14::sorted_unique, std::move(vs), std::move(vi)); - static_assert(std::is_same_v>); - EXPECT_EQ(fm2, decltype(fm2)(sg14::sorted_unique, {{"a",1}, {"b",2}})); } +#endif // __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L if (true) { - // flat_map(sorted_unique_t, Container, Allocator) - std::vector> v; - flat_map fm1(sg14::sorted_unique, v, std::allocator()); - static_assert(std::is_same_v>); -#if __cpp_lib_memory_resource >= 201603 - std::pmr::vector> pv; - // TODO: neither of these lines compiles, and it's unclear what is INTENDED to happen - // flat_map fm2(sg14::sorted_unique, pv, std::allocator()); - // flat_map fm2(sg14::sorted_unique, pv, std::pmr::polymorphic_allocator()); -#endif - } - if (true) { - // flat_map(sorted_unique_t, KeyContainer, MappedContainer, Allocator) - std::vector vi {2,1}; - std::vector vs {"a","b"}; - flat_map fm1(sg14::sorted_unique, vs, vi, std::allocator()); - static_assert(std::is_same_v>); - EXPECT_EQ(fm1, decltype(fm1)(sg14::sorted_unique, {{"a",2}, {"b",1}})); -#if __cpp_lib_memory_resource >= 201603 - std::pmr::vector pvi {1, 2}; - std::pmr::vector pvs {"b","a"}; - flat_map fm2(sg14::sorted_unique, pvi, pvs, std::pmr::polymorphic_allocator()); - static_assert(std::is_same_v, std::pmr::vector, std::pmr::vector>>); - EXPECT_EQ(fm2, decltype(fm2)(sg14::sorted_unique, {{1,"b"}, {2,"a"}})); -#endif - } - if (true) { - // flat_map(InputIterator, InputIterator, Compare = Compare()) + // flat_map(InputIterator, InputIterator) std::vector> v; flat_map fm1(v.begin(), v.end()); static_assert(std::is_same_v>); + flat_map fm2(sg14::sorted_unique, v.begin(), v.end()); + static_assert(std::is_same_v>); std::list> lst; flat_map fm3(lst.begin(), lst.end()); static_assert(std::is_same_v>); @@ -429,18 +353,12 @@ TEST(flat_map, DeductionGuides) EXPECT_EQ(fm5, decltype(fm5)(sg14::sorted_unique, {{1,"c"}, {3,"a"}, {5,"b"}})); } if (true) { - // flat_map(InputIterator, InputIterator, Compare = Compare()) + // flat_map(InputIterator, InputIterator, Compare) std::vector> v; - flat_map fm1(v.begin(), v.end(), std::less()); - static_assert(std::is_same_v>); - int x = 3; - std::pair arr[] = {{1,2}, {2,3}, {3,4}, {4,5}}; - flat_map fm2(arr, arr + 4, [&x](int a, int b){ return (a % x) < (b % x); }); - EXPECT_FALSE(fm2.key_comp()(2, 4)); - x = 10; - EXPECT_TRUE(fm2.key_comp()(2, 4)); - x = 3; - EXPECT_EQ(fm2.begin()[0].first, 3); + flat_map fm1(v.begin(), v.end(), std::less<>()); + static_assert(std::is_same_v>>); + flat_map fm2(sg14::sorted_unique, v.begin(), v.end(), std::less<>()); + static_assert(std::is_same_v>>); std::list> lst; flat_map fm3(lst.begin(), lst.end(), std::greater<>()); static_assert(std::is_same_v>>); @@ -450,77 +368,16 @@ TEST(flat_map, DeductionGuides) static_assert(std::is_same_v>>); #endif std::initializer_list> il = {{1,"c"}, {5,"b"}, {3,"a"}}; - flat_map fm5(il.begin(), il.end(), std::less()); - static_assert(std::is_same_v>); + flat_map fm5(il.begin(), il.end(), std::less<>()); + static_assert(std::is_same_v>>); EXPECT_EQ(fm5, decltype(fm5)(sg14::sorted_unique, {{1,"c"}, {3,"a"}, {5,"b"}})); + std::pair arr[] = {{1,2}, {3,4}, {2,3}, {4,5}}; flat_map fm6(arr, arr + 4, free_function_less); static_assert(std::is_same_v>); EXPECT_EQ(fm6.key_comp(), free_function_less); EXPECT_EQ(fm6, decltype(fm6)(sg14::sorted_unique, {{1,2}, {2,3}, {3,4}, {4,5}}, free_function_less)); } - if (true) { - // flat_map(InputIterator, InputIterator, Compare, Allocator) - std::vector> v; - flat_map fm1(v.begin(), v.end(), std::less(), std::allocator()); - static_assert(std::is_same_v>); - int x = 3; - std::pair arr[] = {{1,2}, {2,3}, {3,4}, {4,5}}; - flat_map fm2(arr, arr + 4, [&x](int a, int b){ return (a % x) < (b % x); }, std::allocator()); - EXPECT_FALSE(fm2.key_comp()(2, 4)); - x = 10; - EXPECT_TRUE(fm2.key_comp()(2, 4)); - x = 3; - EXPECT_EQ(fm2.begin()[0].first, 3); - std::list> lst; - flat_map fm3(lst.begin(), lst.end(), std::greater<>(), std::allocator()); - static_assert(std::is_same_v>>); -#if __cpp_lib_memory_resource >= 201603 - std::pmr::vector> pv; - flat_map fm4(pv.begin(), pv.end(), std::greater<>(), std::allocator()); - static_assert(std::is_same_v>>); - EXPECT_TRUE(!flatmap_is_ctadable_from(0, pv.begin(), pv.end(), std::greater(), std::pmr::polymorphic_allocator())); -#endif - std::initializer_list> il = {{1,"c"}, {5,"b"}, {3,"a"}}; - flat_map fm5(il.begin(), il.end(), std::less(), std::allocator()); - static_assert(std::is_same_v>); - EXPECT_TRUE(( fm5 == decltype(fm5)(sg14::sorted_unique, {{1,"c"}, {3,"a"}, {5,"b"}}) )); - - flat_map fm6(arr, arr + 4, free_function_less, std::allocator()); - static_assert(std::is_same_v>); - EXPECT_TRUE(fm6.key_comp() == free_function_less); - EXPECT_TRUE(( fm6 == decltype(fm6)(sg14::sorted_unique, {{1,2}, {2,3}, {3,4}, {4,5}}, free_function_less) )); - } - if (true) { - // flat_map(InputIterator, InputIterator, Allocator) - } - if (true) { - // flat_map(sorted_unique_t, InputIterator, InputIterator, Compare = Compare()) - } - if (true) { - // flat_map(sorted_unique_t, InputIterator, InputIterator, Compare, Allocator) - } - if (true) { - // flat_map(sorted_unique_t, InputIterator, InputIterator, Allocator) - } - if (true) { - // flat_map(std::initializer_list>, Compare = Compare()) - } - if (true) { - // flat_map(std::initializer_list>, Compare, Allocator) - } - if (true) { - // flat_map(std::initializer_list>, Allocator) - } - if (true) { - // flat_map(sorted_unique_t, std::initializer_list>, Compare = Compare()) - } - if (true) { - // flat_map(sorted_unique_t, std::initializer_list>, Compare, Allocator) - } - if (true) { - // flat_map(sorted_unique_t, std::initializer_list>, Allocator) - } #endif // defined(__cpp_deduction_guides) } @@ -554,16 +411,20 @@ TYPED_TEST(flat_mapt, Construction) EXPECT_TRUE(fs[5] == Str("b")); } for (auto&& fs : { +#if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L + FS(std::from_range, pairs), + FS(std::from_range, pairs, Compare()), +#endif FS({{1, "a"}, {3, "c"}, {5, "b"}}), FS(pairs.begin(), pairs.end()), FS(pairs.rbegin(), pairs.rend()), - FS(pairs, Compare()), FS({{1, "a"}, {3, "c"}, {5, "b"}}, Compare()), FS(pairs.begin(), pairs.end(), Compare()), FS(pairs.rbegin(), pairs.rend(), Compare()), }) { EXPECT_TRUE(std::is_sorted(fs.keys().begin(), fs.keys().end(), fs.key_comp())); EXPECT_TRUE(std::is_sorted(fs.begin(), fs.end(), fs.value_comp())); + EXPECT_EQ(fs.size(), 3u); EXPECT_TRUE(fs.find(0) == fs.end()); EXPECT_TRUE(fs.find(1) != fs.end()); EXPECT_TRUE(fs.find(2) == fs.end()); @@ -577,10 +438,8 @@ TYPED_TEST(flat_mapt, Construction) } if (std::is_sorted(keys.begin(), keys.end(), Compare())) { for (auto&& fs : { - FS(sg14::sorted_unique, pairs), FS(sg14::sorted_unique, pairs.begin(), pairs.end()), FS(sg14::sorted_unique, {{1, "a"}, {3, "c"}, {5, "b"}}), - FS(sg14::sorted_unique, pairs, Compare()), FS(sg14::sorted_unique, pairs.begin(), pairs.end(), Compare()), FS(sg14::sorted_unique, {{1, "a"}, {3, "c"}, {5, "b"}}, Compare()), }) { diff --git a/test/flat_set_test.cpp b/test/flat_set_test.cpp index 3213381..d003c0e 100644 --- a/test/flat_set_test.cpp +++ b/test/flat_set_test.cpp @@ -9,9 +9,6 @@ #if __has_include() #include #endif -#if __has_include() -#include -#endif #include #include @@ -19,6 +16,10 @@ #include #endif +#if __cplusplus >= 202002L +#include +#endif + template struct flat_sett : testing::Test {}; using flat_sett_types = testing::Types<