mirror of
https://gcc.gnu.org/git/gcc.git
synced 2025-01-13 06:23:55 +08:00
libstdc++: Fix view adaptors for mixed-const sentinels and iterators (PR 95322)
The bug report is that transform_view's sentinel<false> cannot be compared to its iterator<true>. The comparison is supposed to use operator==(iterator<Const>, sentinel<Const>) after converting sentinel<false> to sentinel<true>. However, the operator== is a hidden friend so is not a candidate when comparing iterator<true> with sentinel<false>. The required conversion would only happen if we'd found the operator, but we can't find the operator until after the conversion happens. A new LWG issue has been reported, but not yet assigned a number. The solution suggested by Casey Carter is to make the hidden friends of the sentinel types work with iterators of any const-ness, so that no conversions are required. Patrick Palka observed that join_view has a similar problem and a similar fix is used for its sentinel. PR libstdc++/95322 * include/std/ranges (transform_view::_Sentinel): Allow hidden friends to work with _Iterator<true> and _Iterator<false>. (join_view::_Sentinel): Likewise. * testsuite/std/ranges/adaptors/95322.cc: New test.
This commit is contained in:
parent
979e89a9a9
commit
6c2582c040
@ -1853,7 +1853,7 @@ namespace views
|
||||
{ return ranges::iter_swap(__x._M_current, __y._M_current); }
|
||||
|
||||
friend _Iterator<!_Const>;
|
||||
friend _Sentinel<_Const>;
|
||||
template<bool> friend struct _Sentinel;
|
||||
};
|
||||
|
||||
template<bool _Const>
|
||||
@ -1863,13 +1863,15 @@ namespace views
|
||||
using _Parent = __detail::__maybe_const_t<_Const, transform_view>;
|
||||
using _Base = __detail::__maybe_const_t<_Const, _Vp>;
|
||||
|
||||
constexpr range_difference_t<_Base>
|
||||
__distance_from(const _Iterator<_Const>& __i) const
|
||||
{ return _M_end - __i._M_current; }
|
||||
template<bool _Const2>
|
||||
constexpr range_difference_t<_Base>
|
||||
__distance_from(const _Iterator<_Const2>& __i) const
|
||||
{ return _M_end - __i._M_current; }
|
||||
|
||||
constexpr bool
|
||||
__equal(const _Iterator<_Const>& __i) const
|
||||
{ return __i._M_current == _M_end; }
|
||||
template<bool _Const2>
|
||||
constexpr bool
|
||||
__equal(const _Iterator<_Const2>& __i) const
|
||||
{ return __i._M_current == _M_end; }
|
||||
|
||||
sentinel_t<_Base> _M_end = sentinel_t<_Base>();
|
||||
|
||||
@ -1892,19 +1894,26 @@ namespace views
|
||||
base() const
|
||||
{ return _M_end; }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(const _Iterator<_Const>& __x, const _Sentinel& __y)
|
||||
{ return __y.__equal(__x); }
|
||||
template<bool _Const2>
|
||||
requires sentinel_for<sentinel_t<_Base>,
|
||||
iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
|
||||
friend constexpr bool
|
||||
operator==(const _Iterator<_Const2>& __x, const _Sentinel& __y)
|
||||
{ return __y.__equal(__x); }
|
||||
|
||||
friend constexpr range_difference_t<_Base>
|
||||
operator-(const _Iterator<_Const>& __x, const _Sentinel& __y)
|
||||
requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
|
||||
{ return -__y.__distance_from(__x); }
|
||||
template<bool _Const2>
|
||||
requires sized_sentinel_for<sentinel_t<_Base>,
|
||||
iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
|
||||
friend constexpr range_difference_t<_Base>
|
||||
operator-(const _Iterator<_Const2>& __x, const _Sentinel& __y)
|
||||
{ return -__y.__distance_from(__x); }
|
||||
|
||||
friend constexpr range_difference_t<_Base>
|
||||
operator-(const _Sentinel& __y, const _Iterator<_Const>& __x)
|
||||
requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<_Base>>
|
||||
{ return __y.__distance_from(__x); }
|
||||
template<bool _Const2>
|
||||
requires sized_sentinel_for<sentinel_t<_Base>,
|
||||
iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
|
||||
friend constexpr range_difference_t<_Base>
|
||||
operator-(const _Sentinel& __y, const _Iterator<_Const2>& __x)
|
||||
{ return __y.__distance_from(__x); }
|
||||
|
||||
friend _Sentinel<!_Const>;
|
||||
};
|
||||
@ -2571,7 +2580,7 @@ namespace views
|
||||
{ return ranges::iter_swap(__x._M_inner, __y._M_inner); }
|
||||
|
||||
friend _Iterator<!_Const>;
|
||||
friend _Sentinel<_Const>;
|
||||
template<bool> friend struct _Sentinel;
|
||||
};
|
||||
|
||||
template<bool _Const>
|
||||
@ -2581,9 +2590,10 @@ namespace views
|
||||
using _Parent = __detail::__maybe_const_t<_Const, join_view>;
|
||||
using _Base = __detail::__maybe_const_t<_Const, _Vp>;
|
||||
|
||||
constexpr bool
|
||||
__equal(const _Iterator<_Const>& __i) const
|
||||
{ return __i._M_outer == _M_end; }
|
||||
template<bool _Const2>
|
||||
constexpr bool
|
||||
__equal(const _Iterator<_Const2>& __i) const
|
||||
{ return __i._M_outer == _M_end; }
|
||||
|
||||
sentinel_t<_Base> _M_end = sentinel_t<_Base>();
|
||||
|
||||
@ -2601,9 +2611,12 @@ namespace views
|
||||
: _M_end(std::move(__s._M_end))
|
||||
{ }
|
||||
|
||||
friend constexpr bool
|
||||
operator==(const _Iterator<_Const>& __x, const _Sentinel& __y)
|
||||
{ return __y.__equal(__x); }
|
||||
template<bool _Const2>
|
||||
requires sentinel_for<sentinel_t<_Base>,
|
||||
iterator_t<__detail::__maybe_const_t<_Const2, _Vp>>>
|
||||
friend constexpr bool
|
||||
operator==(const _Iterator<_Const2>& __x, const _Sentinel& __y)
|
||||
{ return __y.__equal(__x); }
|
||||
|
||||
friend _Sentinel<!_Const>;
|
||||
};
|
||||
|
@ -15,36 +15,44 @@
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-std=gnu++2a" }
|
||||
// { dg-options "-std=gnu++20" }
|
||||
// { dg-do run { target c++2a } }
|
||||
|
||||
#include <ranges>
|
||||
#include <list>
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_iterators.h>
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
namespace views = std::views;
|
||||
using __gnu_test::test_forward_range;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
std::list container{1, 2, 3, 4, 5};
|
||||
auto v = (container
|
||||
| views::take(3)
|
||||
| views::transform(std::negate{})
|
||||
| views::common);
|
||||
auto i = ranges::cbegin(v);
|
||||
VERIFY( *i == -1 );
|
||||
++i;
|
||||
VERIFY( *i == -2 );
|
||||
++i;
|
||||
VERIFY( *i == -3 );
|
||||
++i;
|
||||
VERIFY( i == ranges::end(v) );
|
||||
// PR libstdc++/95322
|
||||
int a[2]{1, 2};
|
||||
test_forward_range<int> v{a};
|
||||
auto view1 = v | std::views::take(2);
|
||||
auto view2 = view1 | std::views::transform(std::identity{});
|
||||
const bool eq = std::ranges::cbegin(view2) == std::ranges::end(view2);
|
||||
VERIFY( !eq );
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
void
|
||||
test02()
|
||||
{
|
||||
using irange = test_forward_range<int>;
|
||||
|
||||
int a[2]{1, 2};
|
||||
int b[3]{3, 4, 5};
|
||||
irange u[2]{ irange{a}, irange{b} };
|
||||
test_forward_range<irange> v{u};
|
||||
auto view = (std::views::counted(v.begin(), 2)
|
||||
| std::views::transform(std::identity{})
|
||||
| std::views::join);
|
||||
const bool eq = std::ranges::cbegin(view) == std::ranges::end(view);
|
||||
VERIFY( !eq );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test01();
|
||||
test02();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user