As pointed out by Hewill Kang (reporter) in the issue, checking if iterator of the incoming range satisfies __cpp17_input_iterator, may still lead to hard errors inside of insert_range for iterators that satisfies that concept, but specialize iterator_traits without iterator_category typedef (std::common_iterator specialize iterator_traits without iterator_category in some cases).
To address that we instead check if the iterator_traits<It>::iterator_category is present and denote at least input_iterator_tag, using existing __has_input_iter_cat. PR libstdc++/119415 libstdc++-v3/ChangeLog: * include/std/flat_set (_Flat_set_impl:insert_range): Replace __detail::__cpp17_input_iterator with __has_input_iter_cat. * testsuite/23_containers/flat_multiset/1.cc: New tests * testsuite/23_containers/flat_set/1.cc: New tests --- Tested on x86_64-linux. OK for trunk? libstdc++-v3/include/std/flat_set | 2 +- .../23_containers/flat_multiset/1.cc | 57 +++++++++++++++++-- .../testsuite/23_containers/flat_set/1.cc | 57 +++++++++++++++++-- 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/libstdc++-v3/include/std/flat_set b/libstdc++-v3/include/std/flat_set index bab56742ddd..a7b0b8aef15 100644 --- a/libstdc++-v3/include/std/flat_set +++ b/libstdc++-v3/include/std/flat_set @@ -481,7 +481,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if constexpr (requires { _M_cont.insert_range(_M_cont.end(), __rg); }) __it = _M_cont.insert_range(_M_cont.end(), __rg); else if constexpr (ranges::common_range<_Rg> - && __detail::__cpp17_input_iterator<ranges::iterator_t<_Rg>>) + && __has_input_iter_cat<ranges::iterator_t<_Rg>>) __it = _M_cont.insert(_M_cont.end(), ranges::begin(__rg), ranges::end(__rg)); else { diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc index cc31164315a..dc3cecd9720 100644 --- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc +++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc @@ -152,19 +152,64 @@ struct NoInsertRange : std::vector<T> void insert_range(typename std::vector<T>::const_iterator, R&&) = delete; }; +struct NoCatIterator { + using difference_type = int; + using value_type = int; + + NoCatIterator() : v(0) {} + NoCatIterator(int x) : v(x) {} + + int operator*() const + { return v; } + + NoCatIterator& operator++() + { + ++v; + return *this; + } + + NoCatIterator operator++(int) + { + ++v; + return NoCatIterator(v-1); + } + + bool operator==(const NoCatIterator& rhs) const + { return v == rhs.v; } + +private: + int v; +}; + +template<> +struct std::iterator_traits<NoCatIterator> { + using difference_type = int; + using value_type = int; + using iterator_concept = std::input_iterator_tag; + // no iterator_category, happens also for common_iterator +}; + void test07() { + std::flat_multiset<int> s; + std::flat_multiset<int, std::less<int>, NoInsertRange<int>> s2; + + auto r = std::ranges::subrange<NoCatIterator>(1, 6); + s.insert_range(r); + VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) ); + s2.insert_range(r); + VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) ); + #ifdef __SIZEOF_INT128__ // PR libstdc++/119415 - flat_foo::insert_range cannot handle common ranges // on c++20 only iterators - auto r = std::views::iota(__int128(1), __int128(6)); - - std::flat_multiset<int> s; - s.insert_range(r); + auto r2 = std::views::iota(__int128(1), __int128(6)); + s.clear(); + s.insert_range(r2); VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) ); - std::flat_multiset<int, std::less<int>, NoInsertRange<int>> s2; - s2.insert_range(r); + s2.clear(); + s2.insert_range(r2); VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) ); #endif } diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc index 16881d788fc..90f5855859f 100644 --- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc +++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc @@ -167,19 +167,64 @@ struct NoInsertRange : std::vector<T> void insert_range(typename std::vector<T>::const_iterator, R&&) = delete; }; +struct NoCatIterator { + using difference_type = int; + using value_type = int; + + NoCatIterator() : v(0) {} + NoCatIterator(int x) : v(x) {} + + int operator*() const + { return v; } + + NoCatIterator& operator++() + { + ++v; + return *this; + } + + NoCatIterator operator++(int) + { + ++v; + return NoCatIterator(v-1); + } + + bool operator==(const NoCatIterator& rhs) const + { return v == rhs.v; } + +private: + int v; +}; + +template<> +struct std::iterator_traits<NoCatIterator> { + using difference_type = int; + using value_type = int; + using iterator_concept = std::input_iterator_tag; + // no iterator_category, happens also for common_iterator +}; + void test07() { + std::flat_set<int> s; + std::flat_set<int, std::less<int>, NoInsertRange<int>> s2; + + auto r = std::ranges::subrange<NoCatIterator>(1, 6); + s.insert_range(r); + VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) ); + s2.insert_range(r); + VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) ); + #ifdef __SIZEOF_INT128__ // PR libstdc++/119415 - flat_foo::insert_range cannot handle common ranges // on c++20 only iterators - auto r = std::views::iota(__int128(1), __int128(6)); - - std::flat_set<int> s; - s.insert_range(r); + auto r2 = std::views::iota(__int128(1), __int128(6)); + s.clear(); + s.insert_range(r2); VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) ); - std::flat_set<int, std::less<int>, NoInsertRange<int>> s2; - s2.insert_range(r); + s2.clear(); + s2.insert_range(r2); VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) ); #endif } -- 2.48.1