libstdc++: Fix handling of common cpp20-only ranges for flat sets [PR119415]

These patch add check to verify if common range iterators satisfies
Cpp17LegacyIterator requirements (__detail::__cpp17_input_iterator),
before invoking overloads of insert that accepts two iterators.
As such overloads existed before c++20 iterators were introduced,
they commonly assume existence of iterator_traits<..>::iterator_category,
and passing a cpp20-only iterators, leads to hard errors.

In case if user-defined container wants to support more efficient
insertion in such cases, it should provided insert_range method,
as in the case of standard containers.

	PR libstdc++/119415

libstdc++-v3/ChangeLog:

	* include/std/flat_set (_Flat_set_impl:insert_range):
	Add __detail::__cpp17_input_iterator check.
	* testsuite/23_containers/flat_multiset/1.cc: New tests
	* testsuite/23_containers/flat_set/1.cc: New tests

Reviewed-by: Patrick Palka <ppalka@redhat.com>, Jonathan Wakely <jwakely@redhat.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
Tomasz Kamiński 2025-03-24 18:04:28 +01:00
parent 698e337bec
commit 4d1b196956
3 changed files with 56 additions and 1 deletions

View file

@ -480,7 +480,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
typename container_type::iterator __it;
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>)
else if constexpr (ranges::common_range<_Rg>
&& __detail::__cpp17_input_iterator<ranges::iterator_t<_Rg>>)
__it = _M_cont.insert(_M_cont.end(), ranges::begin(__rg), ranges::end(__rg));
else
{

View file

@ -143,6 +143,32 @@ test06()
VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) );
}
template<typename T>
struct NoInsertRange : std::vector<T>
{
using std::vector<T>::vector;
template<typename It, typename R>
void insert_range(typename std::vector<T>::const_iterator, R&&) = delete;
};
void test07()
{
#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);
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);
VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) );
#endif
}
int
main()
{
@ -153,4 +179,5 @@ main()
test04();
test05();
test06();
test07();
}

View file

@ -158,6 +158,32 @@ test06()
VERIFY( std::ranges::equal(s, (int[]){1, 2, 3, 4, 5}) );
}
template<typename T>
struct NoInsertRange : std::vector<T>
{
using std::vector<T>::vector;
template<typename It, typename R>
void insert_range(typename std::vector<T>::const_iterator, R&&) = delete;
};
void test07()
{
#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);
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);
VERIFY( std::ranges::equal(s2, (int[]){1, 2, 3, 4, 5}) );
#endif
}
int
main()
{
@ -168,4 +194,5 @@ main()
test04();
test05();
test06();
test07();
}