mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-21 05:00:23 +08:00
libstdc++: Implement P2278R4 "cbegin should always return a constant iterator"
This also implements the approved follow-up LWG issues 3765, 3766, 3769, 3770, 3811, 3850, 3853, 3862 and 3872. libstdc++-v3/ChangeLog: * include/bits/ranges_base.h (const_iterator_t): Define for C++23. (const_sentinel_t): Likewise. (range_const_reference_t): Likewise. (constant_range): Likewise. (__cust_access::__possibly_const_range): Likewise, replacing ... (__cust_access::__as_const): ... this. (__cust_access::_CBegin::operator()): Redefine for C++23 as per P2278R4. (__cust_access::_CEnd::operator()): Likewise. (__cust_access::_CRBegin::operator()): Likewise. (__cust_access::_CREnd::operator()): Likewise. (__cust_access::_CData::operator()): Likewise. * include/bits/ranges_util.h (ranges::__detail::__different_from): Make it an alias of std::__detail::__different_from. (view_interface::cbegin): Define for C++23. (view_interface::cend): Likewise. * include/bits/stl_iterator.h (__detail::__different_from): Define. (iter_const_reference_t): Define for C++23. (__detail::__constant_iterator): Likewise. (__detail::__is_const_iterator): Likewise. (__detail::__not_a_const_iterator): Likewise. (__detail::__iter_const_rvalue_reference_t): Likewise. (__detail::__basic_const_iter_cat):: Likewise. (const_iterator): Likewise. (__detail::__const_sentinel): Likewise. (const_sentinel): Likewise. (basic_const_iterator): Likewise. (common_type<basic_const_iterator<_Tp>, _Up>): Likewise. (common_type<_Up, basic_const_iterator<_Tp>>): Likewise. (common_type<basic_const_iterator<_Tp>, basic_const_iterator<Up>>): Likewise. (make_const_iterator): Define for C++23. (make_const_sentinel): Likewise. * include/std/ranges (__cpp_lib_ranges_as_const): Likewise. (as_const_view): Likewise. (enable_borrowed_range<as_const_view>): Likewise. (views::__detail::__is_ref_view): Likewise. (views::__detail::__can_is_const_view): Likewise. (views::_AsConst, views::as_const): Likewise. * include/std/span (span::const_iterator): Likewise. (span::const_reverse_iterator): Likewise. (span::cbegin): Likewise. (span::cend): Likewise. (span::crbegin): Likewise. (span::crend): Likewise. * include/std/version (__cpp_lib_ranges_as_const): Likewise. * testsuite/std/ranges/adaptors/join.cc (test06): Adjust to behave independently of C++20 vs C++23. * testsuite/std/ranges/version_c++23.cc: Verify value of __cpp_lib_ranges_as_const macro. * testsuite/24_iterators/const_iterator/1.cc: New test. * testsuite/std/ranges/adaptors/as_const/1.cc: New test.
This commit is contained in:
parent
2ab0d83e88
commit
0d94c6df18
@ -515,6 +515,17 @@ namespace ranges
|
||||
template<range _Range>
|
||||
using sentinel_t = decltype(ranges::end(std::declval<_Range&>()));
|
||||
|
||||
#if __cplusplus > 202002L
|
||||
template<range _Range>
|
||||
using const_iterator_t = const_iterator<iterator_t<_Range>>;
|
||||
|
||||
template<range _Range>
|
||||
using const_sentinel_t = const_sentinel<sentinel_t<_Range>>;
|
||||
|
||||
template<range _Range>
|
||||
using range_const_reference_t = iter_const_reference_t<iterator_t<_Range>>;
|
||||
#endif
|
||||
|
||||
template<range _Range>
|
||||
using range_difference_t = iter_difference_t<iterator_t<_Range>>;
|
||||
|
||||
@ -607,8 +618,25 @@ namespace ranges
|
||||
concept common_range
|
||||
= range<_Tp> && same_as<iterator_t<_Tp>, sentinel_t<_Tp>>;
|
||||
|
||||
#if __cplusplus > 202002L
|
||||
template<typename _Tp>
|
||||
concept constant_range
|
||||
= input_range<_Tp> && std::__detail::__constant_iterator<iterator_t<_Tp>>;
|
||||
#endif
|
||||
|
||||
namespace __cust_access
|
||||
{
|
||||
#if __cplusplus > 202020L
|
||||
template<typename _Range>
|
||||
constexpr auto&
|
||||
__possibly_const_range(_Range& __r) noexcept
|
||||
{
|
||||
if constexpr (constant_range<const _Range> && !constant_range<_Range>)
|
||||
return const_cast<const _Range&>(__r);
|
||||
else
|
||||
return __r;
|
||||
}
|
||||
#else
|
||||
// If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
|
||||
template<typename _To, typename _Tp>
|
||||
constexpr decltype(auto)
|
||||
@ -621,9 +649,24 @@ namespace ranges
|
||||
else
|
||||
return static_cast<const _Tp&&>(__t);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct _CBegin
|
||||
{
|
||||
#if __cplusplus > 202002L
|
||||
template<__maybe_borrowed_range _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(std::make_const_iterator
|
||||
(ranges::begin(__cust_access::__possibly_const_range(__t)))))
|
||||
requires requires { std::make_const_iterator
|
||||
(ranges::begin(__cust_access::__possibly_const_range(__t))); }
|
||||
{
|
||||
auto& __r = __cust_access::__possibly_const_range(__t);
|
||||
return const_iterator_t<decltype(__r)>(ranges::begin(__r));
|
||||
}
|
||||
#else
|
||||
template<typename _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
@ -633,10 +676,25 @@ namespace ranges
|
||||
{
|
||||
return _Begin{}(__cust_access::__as_const<_Tp>(__e));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct _CEnd final
|
||||
{
|
||||
#if __cplusplus > 202002L
|
||||
template<__maybe_borrowed_range _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(std::make_const_sentinel
|
||||
(ranges::end(__cust_access::__possibly_const_range(__t)))))
|
||||
requires requires { std::make_const_sentinel
|
||||
(ranges::end(__cust_access::__possibly_const_range(__t))); }
|
||||
{
|
||||
auto& __r = __cust_access::__possibly_const_range(__t);
|
||||
return const_sentinel_t<decltype(__r)>(ranges::end(__r));
|
||||
}
|
||||
#else
|
||||
template<typename _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
@ -646,10 +704,25 @@ namespace ranges
|
||||
{
|
||||
return _End{}(__cust_access::__as_const<_Tp>(__e));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct _CRBegin
|
||||
{
|
||||
#if __cplusplus > 202002L
|
||||
template<__maybe_borrowed_range _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(std::make_const_iterator
|
||||
(ranges::rbegin(__cust_access::__possibly_const_range(__t)))))
|
||||
requires requires { std::make_const_iterator
|
||||
(ranges::rbegin(__cust_access::__possibly_const_range(__t))); }
|
||||
{
|
||||
auto& __r = __cust_access::__possibly_const_range(__t);
|
||||
return const_iterator<decltype(ranges::rbegin(__r))>(ranges::rbegin(__r));
|
||||
}
|
||||
#else
|
||||
template<typename _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
@ -659,10 +732,25 @@ namespace ranges
|
||||
{
|
||||
return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct _CREnd
|
||||
{
|
||||
#if __cplusplus > 202002L
|
||||
template<__maybe_borrowed_range _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(std::make_const_sentinel
|
||||
(ranges::rend(__cust_access::__possibly_const_range(__t)))))
|
||||
requires requires { std::make_const_sentinel
|
||||
(ranges::rend(__cust_access::__possibly_const_range(__t))); }
|
||||
{
|
||||
auto& __r = __cust_access::__possibly_const_range(__t);
|
||||
return const_sentinel<decltype(ranges::rend(__r))>(ranges::rend(__r));
|
||||
}
|
||||
#else
|
||||
template<typename _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
@ -672,10 +760,20 @@ namespace ranges
|
||||
{
|
||||
return _REnd{}(__cust_access::__as_const<_Tp>(__e));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct _CData
|
||||
{
|
||||
#if __cplusplus > 202002L
|
||||
template<__maybe_borrowed_range _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr const auto*
|
||||
operator()(_Tp&& __t) const
|
||||
noexcept(noexcept(ranges::data(__cust_access::__possibly_const_range(__t))))
|
||||
requires requires { ranges::data(__cust_access::__possibly_const_range(__t)); }
|
||||
{ return ranges::data(__cust_access::__possibly_const_range(__t)); }
|
||||
#else
|
||||
template<typename _Tp>
|
||||
[[nodiscard]]
|
||||
constexpr auto
|
||||
@ -685,6 +783,7 @@ namespace ranges
|
||||
{
|
||||
return _Data{}(__cust_access::__as_const<_Tp>(__e));
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace __cust_access
|
||||
|
@ -53,9 +53,7 @@ namespace ranges
|
||||
concept __has_arrow = input_iterator<_It>
|
||||
&& (is_pointer_v<_It> || requires(_It __it) { __it.operator->(); });
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
concept __different_from
|
||||
= !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
|
||||
using std::__detail::__different_from;
|
||||
} // namespace __detail
|
||||
|
||||
/// The ranges::view_interface class template
|
||||
@ -192,6 +190,24 @@ namespace ranges
|
||||
constexpr decltype(auto)
|
||||
operator[](range_difference_t<_Range> __n) const
|
||||
{ return ranges::begin(_M_derived())[__n]; }
|
||||
|
||||
#if __cplusplus > 202002L
|
||||
constexpr auto
|
||||
cbegin() requires input_range<_Derived>
|
||||
{ return ranges::cbegin(_M_derived()); }
|
||||
|
||||
constexpr auto
|
||||
cbegin() const requires input_range<const _Derived>
|
||||
{ return ranges::cbegin(_M_derived()); }
|
||||
|
||||
constexpr auto
|
||||
cend() requires input_range<_Derived>
|
||||
{ return ranges::cend(_M_derived()); }
|
||||
|
||||
constexpr auto
|
||||
cend() const requires input_range<const _Derived>
|
||||
{ return ranges::cend(_M_derived()); }
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace __detail
|
||||
|
@ -102,6 +102,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
template<typename _Cat, typename _Limit, typename _Otherwise = _Cat>
|
||||
using __clamp_iter_cat
|
||||
= __conditional_t<derived_from<_Cat, _Limit>, _Limit, _Otherwise>;
|
||||
|
||||
template<typename _Tp, typename _Up>
|
||||
concept __different_from
|
||||
= !same_as<remove_cvref_t<_Tp>, remove_cvref_t<_Up>>;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2578,6 +2582,369 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
add_pointer_t<iter_reference_t<_It>>,
|
||||
void>;
|
||||
};
|
||||
|
||||
#if __cplusplus > 202020L
|
||||
template<indirectly_readable _It>
|
||||
using iter_const_reference_t
|
||||
= common_reference_t<const iter_value_t<_It>&&, iter_reference_t<_It>>;
|
||||
|
||||
template<input_iterator _It> class basic_const_iterator;
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
template<typename _It>
|
||||
concept __constant_iterator = input_iterator<_It>
|
||||
&& same_as<iter_const_reference_t<_It>, iter_reference_t<_It>>;
|
||||
|
||||
template<typename _Tp>
|
||||
inline constexpr bool __is_const_iterator = false;
|
||||
|
||||
template<typename _It>
|
||||
inline constexpr bool __is_const_iterator<basic_const_iterator<_It>> = true;
|
||||
|
||||
template<typename _Tp>
|
||||
concept __not_a_const_iterator = !__is_const_iterator<_Tp>;
|
||||
|
||||
template<indirectly_readable _It>
|
||||
using __iter_const_rvalue_reference_t
|
||||
= common_reference_t<const iter_value_t<_It>&&, iter_rvalue_reference_t<_It>>;
|
||||
|
||||
template<typename _It>
|
||||
struct __basic_const_iterator_iter_cat
|
||||
{ };
|
||||
|
||||
template<forward_iterator _It>
|
||||
struct __basic_const_iterator_iter_cat<_It>
|
||||
{ using iterator_category = iterator_traits<_It>::iterator_category; };
|
||||
} // namespace detail
|
||||
|
||||
template<input_iterator _It>
|
||||
using const_iterator
|
||||
= __conditional_t<__detail::__constant_iterator<_It>, _It, basic_const_iterator<_It>>;
|
||||
|
||||
namespace __detail
|
||||
{
|
||||
template<typename _Sent>
|
||||
struct __const_sentinel
|
||||
{ using type = _Sent; };
|
||||
|
||||
template<input_iterator _Sent>
|
||||
struct __const_sentinel<_Sent>
|
||||
{ using type = const_iterator<_Sent>; };
|
||||
} // namespace __detail
|
||||
|
||||
template<semiregular _Sent>
|
||||
using const_sentinel = typename __detail::__const_sentinel<_Sent>::type;
|
||||
|
||||
template<input_iterator _It>
|
||||
class basic_const_iterator
|
||||
: public __detail::__basic_const_iterator_iter_cat<_It>
|
||||
{
|
||||
_It _M_current = _It();
|
||||
using __reference = iter_const_reference_t<_It>;
|
||||
using __rvalue_reference = __detail::__iter_const_rvalue_reference_t<_It>;
|
||||
|
||||
static auto
|
||||
_S_iter_concept()
|
||||
{
|
||||
if constexpr (contiguous_iterator<_It>)
|
||||
return contiguous_iterator_tag{};
|
||||
else if constexpr (random_access_iterator<_It>)
|
||||
return random_access_iterator_tag{};
|
||||
else if constexpr (bidirectional_iterator<_It>)
|
||||
return bidirectional_iterator_tag{};
|
||||
else if constexpr (forward_iterator<_It>)
|
||||
return forward_iterator_tag{};
|
||||
else
|
||||
return input_iterator_tag{};
|
||||
}
|
||||
|
||||
template<input_iterator _It2> friend class basic_const_iterator;
|
||||
|
||||
public:
|
||||
using iterator_concept = decltype(_S_iter_concept());
|
||||
using value_type = iter_value_t<_It>;
|
||||
using difference_type = iter_difference_t<_It>;
|
||||
|
||||
basic_const_iterator() requires default_initializable<_It> = default;
|
||||
|
||||
constexpr
|
||||
basic_const_iterator(_It __current)
|
||||
noexcept(is_nothrow_move_constructible_v<_It>)
|
||||
: _M_current(std::move(__current))
|
||||
{ }
|
||||
|
||||
template<convertible_to<_It> _It2>
|
||||
constexpr
|
||||
basic_const_iterator(basic_const_iterator<_It2> __current)
|
||||
noexcept(is_nothrow_constructible_v<_It, _It2>)
|
||||
: _M_current(std::move(__current._M_current))
|
||||
{ }
|
||||
|
||||
template<__detail::__different_from<basic_const_iterator> _Tp>
|
||||
requires convertible_to<_Tp, _It>
|
||||
constexpr
|
||||
basic_const_iterator(_Tp&& __current)
|
||||
noexcept(is_nothrow_constructible_v<_It, _Tp>)
|
||||
: _M_current(std::forward<_Tp>(__current))
|
||||
{ }
|
||||
|
||||
constexpr const _It&
|
||||
base() const & noexcept
|
||||
{ return _M_current; }
|
||||
|
||||
constexpr _It
|
||||
base() &&
|
||||
noexcept(is_nothrow_move_constructible_v<_It>)
|
||||
{ return std::move(_M_current); }
|
||||
|
||||
constexpr __reference
|
||||
operator*() const
|
||||
noexcept(noexcept(static_cast<__reference>(*_M_current)))
|
||||
{ return static_cast<__reference>(*_M_current); }
|
||||
|
||||
constexpr const auto*
|
||||
operator->() const
|
||||
noexcept(contiguous_iterator<_It> || noexcept(*_M_current))
|
||||
requires is_lvalue_reference_v<iter_reference_t<_It>>
|
||||
&& same_as<remove_cvref_t<iter_reference_t<_It>>, value_type>
|
||||
{
|
||||
if constexpr (contiguous_iterator<_It>)
|
||||
return std::to_address(_M_current);
|
||||
else
|
||||
return std::__addressof(*_M_current);
|
||||
}
|
||||
|
||||
constexpr basic_const_iterator&
|
||||
operator++()
|
||||
noexcept(noexcept(++_M_current))
|
||||
{
|
||||
++_M_current;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr void
|
||||
operator++(int)
|
||||
noexcept(noexcept(++_M_current))
|
||||
{ ++_M_current; }
|
||||
|
||||
constexpr basic_const_iterator
|
||||
operator++(int)
|
||||
noexcept(noexcept(++*this) && is_nothrow_copy_constructible_v<basic_const_iterator>)
|
||||
requires forward_iterator<_It>
|
||||
{
|
||||
auto __tmp = *this;
|
||||
++*this;
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
constexpr basic_const_iterator&
|
||||
operator--()
|
||||
noexcept(noexcept(--_M_current))
|
||||
requires bidirectional_iterator<_It>
|
||||
{
|
||||
--_M_current;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr basic_const_iterator
|
||||
operator--(int)
|
||||
noexcept(noexcept(--*this) && is_nothrow_copy_constructible_v<basic_const_iterator>)
|
||||
requires bidirectional_iterator<_It>
|
||||
{
|
||||
auto __tmp = *this;
|
||||
--*this;
|
||||
return __tmp;
|
||||
}
|
||||
|
||||
constexpr basic_const_iterator&
|
||||
operator+=(difference_type __n)
|
||||
noexcept(noexcept(_M_current += __n))
|
||||
requires random_access_iterator<_It>
|
||||
{
|
||||
_M_current += __n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr basic_const_iterator&
|
||||
operator-=(difference_type __n)
|
||||
noexcept(noexcept(_M_current -= __n))
|
||||
requires random_access_iterator<_It>
|
||||
{
|
||||
_M_current -= __n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr __reference
|
||||
operator[](difference_type __n) const
|
||||
noexcept(noexcept(static_cast<__reference>(_M_current[__n])))
|
||||
requires random_access_iterator<_It>
|
||||
{ return static_cast<__reference>(_M_current[__n]); }
|
||||
|
||||
template<sentinel_for<_It> _Sent>
|
||||
constexpr bool
|
||||
operator==(const _Sent& __s) const
|
||||
noexcept(noexcept(_M_current == __s))
|
||||
{ return _M_current == __s; }
|
||||
|
||||
constexpr bool
|
||||
operator<(const basic_const_iterator& __y) const
|
||||
noexcept(noexcept(_M_current < __y._M_current))
|
||||
requires random_access_iterator<_It>
|
||||
{ return _M_current < __y._M_current; }
|
||||
|
||||
constexpr bool
|
||||
operator>(const basic_const_iterator& __y) const
|
||||
noexcept(noexcept(_M_current > __y._M_current))
|
||||
requires random_access_iterator<_It>
|
||||
{ return _M_current > __y._M_current; }
|
||||
|
||||
constexpr bool
|
||||
operator<=(const basic_const_iterator& __y) const
|
||||
noexcept(noexcept(_M_current <= __y._M_current))
|
||||
requires random_access_iterator<_It>
|
||||
{ return _M_current <= __y._M_current; }
|
||||
|
||||
constexpr bool
|
||||
operator>=(const basic_const_iterator& __y) const
|
||||
noexcept(noexcept(_M_current >= __y._M_current))
|
||||
requires random_access_iterator<_It>
|
||||
{ return _M_current >= __y._M_current; }
|
||||
|
||||
constexpr auto
|
||||
operator<=>(const basic_const_iterator& __y) const
|
||||
noexcept(noexcept(_M_current <=> __y._M_current))
|
||||
requires random_access_iterator<_It> && three_way_comparable<_It>
|
||||
{ return _M_current <=> __y._M_current; }
|
||||
|
||||
template<__detail::__different_from<basic_const_iterator> _It2>
|
||||
constexpr bool
|
||||
operator<(const _It2& __y) const
|
||||
noexcept(noexcept(_M_current < __y))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return _M_current < __y; }
|
||||
|
||||
template<__detail::__different_from<basic_const_iterator> _It2>
|
||||
constexpr bool
|
||||
operator>(const _It2& __y) const
|
||||
noexcept(noexcept(_M_current > __y))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return _M_current > __y; }
|
||||
|
||||
template<__detail::__different_from<basic_const_iterator> _It2>
|
||||
constexpr bool
|
||||
operator<=(const _It2& __y) const
|
||||
noexcept(noexcept(_M_current <= __y))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return _M_current <= __y; }
|
||||
|
||||
template<__detail::__different_from<basic_const_iterator> _It2>
|
||||
constexpr bool
|
||||
operator>=(const _It2& __y) const
|
||||
noexcept(noexcept(_M_current >= __y))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return _M_current >= __y; }
|
||||
|
||||
template<__detail::__different_from<basic_const_iterator> _It2>
|
||||
constexpr auto
|
||||
operator<=>(const _It2& __y) const
|
||||
noexcept(noexcept(_M_current <=> __y))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
&& three_way_comparable_with<_It, _It2>
|
||||
{ return _M_current <=> __y; }
|
||||
|
||||
template<__detail::__not_a_const_iterator _It2>
|
||||
friend constexpr bool
|
||||
operator<(const _It2& __x, const basic_const_iterator& __y)
|
||||
noexcept(noexcept(__x < __y._M_current))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return __x < __y._M_current; }
|
||||
|
||||
template<__detail::__not_a_const_iterator _It2>
|
||||
friend constexpr bool
|
||||
operator>(const _It2& __x, const basic_const_iterator& __y)
|
||||
noexcept(noexcept(__x > __y._M_current))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return __x > __y._M_current; }
|
||||
|
||||
template<__detail::__not_a_const_iterator _It2>
|
||||
friend constexpr bool
|
||||
operator<=(const _It2& __x, const basic_const_iterator& __y)
|
||||
noexcept(noexcept(__x <= __y._M_current))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return __x <= __y._M_current; }
|
||||
|
||||
template<__detail::__not_a_const_iterator _It2>
|
||||
friend constexpr bool
|
||||
operator>=(const _It2& __x, const basic_const_iterator& __y)
|
||||
noexcept(noexcept(__x >= __y._M_current))
|
||||
requires random_access_iterator<_It> && totally_ordered_with<_It, _It2>
|
||||
{ return __x >= __y._M_current; }
|
||||
|
||||
friend constexpr basic_const_iterator
|
||||
operator+(const basic_const_iterator& __i, difference_type __n)
|
||||
noexcept(noexcept(basic_const_iterator(__i._M_current + __n)))
|
||||
requires random_access_iterator<_It>
|
||||
{ return basic_const_iterator(__i._M_current + __n); }
|
||||
|
||||
friend constexpr basic_const_iterator
|
||||
operator+(difference_type __n, const basic_const_iterator& __i)
|
||||
noexcept(noexcept(basic_const_iterator(__i._M_current + __n)))
|
||||
requires random_access_iterator<_It>
|
||||
{ return basic_const_iterator(__i._M_current + __n); }
|
||||
|
||||
friend constexpr basic_const_iterator
|
||||
operator-(const basic_const_iterator& __i, difference_type __n)
|
||||
noexcept(noexcept(basic_const_iterator(__i._M_current - __n)))
|
||||
requires random_access_iterator<_It>
|
||||
{ return basic_const_iterator(__i._M_current - __n); }
|
||||
|
||||
template<sized_sentinel_for<_It> _Sent>
|
||||
constexpr difference_type
|
||||
operator-(const _Sent& __y) const
|
||||
noexcept(noexcept(_M_current - __y))
|
||||
{ return _M_current - __y; }
|
||||
|
||||
template<__detail::__not_a_const_iterator _Sent>
|
||||
requires sized_sentinel_for<_Sent, _It>
|
||||
friend constexpr difference_type
|
||||
operator-(const _Sent& __x, const basic_const_iterator& __y)
|
||||
noexcept(noexcept(__x - __y._M_current))
|
||||
{ return __x - __y._M_current; }
|
||||
|
||||
friend constexpr __rvalue_reference
|
||||
iter_move(const basic_const_iterator& __i)
|
||||
noexcept(noexcept(static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current))))
|
||||
{ return static_cast<__rvalue_reference>(ranges::iter_move(__i._M_current)); }
|
||||
};
|
||||
|
||||
template<typename _Tp, common_with<_Tp> _Up>
|
||||
requires input_iterator<common_type_t<_Tp, _Up>>
|
||||
struct common_type<basic_const_iterator<_Tp>, _Up>
|
||||
{ using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
|
||||
|
||||
template<typename _Tp, common_with<_Tp> _Up>
|
||||
requires input_iterator<common_type_t<_Tp, _Up>>
|
||||
struct common_type<_Up, basic_const_iterator<_Tp>>
|
||||
{ using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
|
||||
|
||||
template<typename _Tp, common_with<_Tp> _Up>
|
||||
requires input_iterator<common_type_t<_Tp, _Up>>
|
||||
struct common_type<basic_const_iterator<_Tp>, basic_const_iterator<_Up>>
|
||||
{ using type = basic_const_iterator<common_type_t<_Tp, _Up>>; };
|
||||
|
||||
template<input_iterator _It>
|
||||
constexpr const_iterator<_It>
|
||||
make_const_iterator(_It __it)
|
||||
noexcept(is_nothrow_convertible_v<_It, const_iterator<_It>>)
|
||||
{ return __it; }
|
||||
|
||||
template<semiregular _Sent>
|
||||
constexpr const_sentinel<_Sent>
|
||||
make_const_sentinel(_Sent __s)
|
||||
noexcept(is_nothrow_convertible_v<_Sent, const_sentinel<_Sent>>)
|
||||
{ return __s; }
|
||||
#endif // C++23
|
||||
#endif // C++20
|
||||
|
||||
/// @} group iterators
|
||||
|
@ -8929,6 +8929,112 @@ namespace views::__adaptor
|
||||
|
||||
inline constexpr _Enumerate enumerate;
|
||||
}
|
||||
|
||||
#define __cpp_lib_ranges_as_const 202207L
|
||||
|
||||
template<view _Vp>
|
||||
requires input_range<_Vp>
|
||||
class as_const_view : public view_interface<as_const_view<_Vp>>
|
||||
{
|
||||
_Vp _M_base = _Vp();
|
||||
|
||||
public:
|
||||
as_const_view() requires default_initializable<_Vp> = default;
|
||||
|
||||
constexpr explicit
|
||||
as_const_view(_Vp __base)
|
||||
noexcept(is_nothrow_move_constructible_v<_Vp>)
|
||||
: _M_base(std::move(__base))
|
||||
{ }
|
||||
|
||||
constexpr _Vp
|
||||
base() const &
|
||||
noexcept(is_nothrow_copy_constructible_v<_Vp>)
|
||||
requires copy_constructible<_Vp>
|
||||
{ return _M_base; }
|
||||
|
||||
constexpr _Vp
|
||||
base() &&
|
||||
noexcept(is_nothrow_move_constructible_v<_Vp>)
|
||||
{ return std::move(_M_base); }
|
||||
|
||||
constexpr auto
|
||||
begin() requires (!__detail::__simple_view<_Vp>)
|
||||
{ return ranges::cbegin(_M_base); }
|
||||
|
||||
constexpr auto
|
||||
begin() const requires range<const _Vp>
|
||||
{ return ranges::cbegin(_M_base); }
|
||||
|
||||
constexpr auto
|
||||
end() requires (!__detail::__simple_view<_Vp>)
|
||||
{ return ranges::cend(_M_base); }
|
||||
|
||||
constexpr auto
|
||||
end() const requires range<const _Vp>
|
||||
{ return ranges::cend(_M_base); }
|
||||
|
||||
constexpr auto
|
||||
size() requires sized_range<_Vp>
|
||||
{ return ranges::size(_M_base); }
|
||||
|
||||
constexpr auto
|
||||
size() const requires sized_range<const _Vp>
|
||||
{ return ranges::size(_M_base); }
|
||||
};
|
||||
|
||||
template<typename _Range>
|
||||
as_const_view(_Range&&) -> as_const_view<views::all_t<_Range>>;
|
||||
|
||||
template<typename _Tp>
|
||||
inline constexpr bool enable_borrowed_range<as_const_view<_Tp>>
|
||||
= enable_borrowed_range<_Tp>;
|
||||
|
||||
namespace views
|
||||
{
|
||||
namespace __detail
|
||||
{
|
||||
template<typename _Tp>
|
||||
inline constexpr bool __is_ref_view = false;
|
||||
|
||||
template<typename _Range>
|
||||
inline constexpr bool __is_ref_view<ref_view<_Range>> = true;
|
||||
|
||||
template<typename _Range>
|
||||
concept __can_as_const_view = requires { as_const_view(std::declval<_Range>()); };
|
||||
}
|
||||
|
||||
struct _AsConst : __adaptor::_RangeAdaptorClosure
|
||||
{
|
||||
template<viewable_range _Range>
|
||||
constexpr auto
|
||||
operator()(_Range&& __r) const
|
||||
noexcept(noexcept(as_const_view(std::declval<_Range>())))
|
||||
requires __detail::__can_as_const_view<_Range>
|
||||
{
|
||||
using _Tp = remove_cvref_t<_Range>;
|
||||
using element_type = remove_reference_t<range_reference_t<_Range>>;
|
||||
if constexpr (constant_range<views::all_t<_Range>>)
|
||||
return views::all(std::forward<_Range>(__r));
|
||||
else if constexpr (__detail::__is_empty_view<_Tp>)
|
||||
return views::empty<const element_type>;
|
||||
else if constexpr (std::__detail::__is_span<_Tp>)
|
||||
return span<const element_type, _Tp::extent>(std::forward<_Range>(__r));
|
||||
else if constexpr (__detail::__is_ref_view<_Tp>
|
||||
&& constant_range<const element_type>)
|
||||
return ref_view(static_cast<const element_type&>
|
||||
(std::forward<_Range>(__r).base()));
|
||||
else if constexpr (is_lvalue_reference_v<_Range>
|
||||
&& constant_range<_Tp>
|
||||
&& !view<_Tp>)
|
||||
return ref_view(static_cast<const _Tp&>(__r));
|
||||
else
|
||||
return as_const_view(std::forward<_Range>(__r));
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr _AsConst as_const;
|
||||
}
|
||||
#endif // C++23
|
||||
} // namespace ranges
|
||||
|
||||
|
@ -137,6 +137,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
using const_reference = const element_type&;
|
||||
using iterator = __gnu_cxx::__normal_iterator<pointer, span>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
#if __cplusplus > 202002L
|
||||
using const_iterator = std::const_iterator<iterator>;
|
||||
using const_reverse_iterator = std::const_iterator<reverse_iterator>;
|
||||
#endif
|
||||
|
||||
// member constants
|
||||
static constexpr size_t extent = _Extent;
|
||||
@ -301,6 +305,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
rend() const noexcept
|
||||
{ return reverse_iterator(this->begin()); }
|
||||
|
||||
#if __cplusplus > 202002L
|
||||
constexpr const_iterator
|
||||
cbegin() const noexcept
|
||||
{ return begin(); }
|
||||
|
||||
constexpr const_iterator
|
||||
cend() const noexcept
|
||||
{ return end(); }
|
||||
|
||||
constexpr const_reverse_iterator
|
||||
crbegin() const noexcept
|
||||
{ return rbegin(); }
|
||||
|
||||
constexpr const_reverse_iterator
|
||||
crend() const noexcept
|
||||
{ return rend(); }
|
||||
#endif
|
||||
|
||||
// subviews
|
||||
|
||||
template<size_t _Count>
|
||||
|
@ -339,6 +339,7 @@
|
||||
#define __cpp_lib_ranges_stride 202207L
|
||||
#define __cpp_lib_ranges_cartesian_product 202207L
|
||||
#define __cpp_lib_ranges_as_rvalue 202207L
|
||||
#define __cpp_lib_ranges_as_const 202207L
|
||||
#define __cpp_lib_ranges_enumerate 202302L
|
||||
#define __cpp_lib_fold 202207L
|
||||
#if __cpp_constexpr_dynamic_alloc
|
||||
|
140
libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
Normal file
140
libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc
Normal file
@ -0,0 +1,140 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <iterator>
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <testsuite_iterators.h>
|
||||
|
||||
using __gnu_test::test_input_range;
|
||||
using __gnu_test::test_forward_range;
|
||||
using __gnu_test::test_bidirectional_range;
|
||||
using __gnu_test::test_random_access_range;
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
|
||||
template<class Iter, bool Const>
|
||||
void
|
||||
test01()
|
||||
{
|
||||
if constexpr (Const)
|
||||
{
|
||||
static_assert( std::same_as<std::const_iterator<Iter>, Iter> );
|
||||
static_assert( std::same_as<std::const_sentinel<Iter>, Iter> );
|
||||
static_assert( std::same_as<std::iter_const_reference_t<Iter>,
|
||||
std::iter_reference_t<Iter>> );
|
||||
}
|
||||
else
|
||||
{
|
||||
using Wrapped = std::basic_const_iterator<Iter>;
|
||||
|
||||
static_assert( std::same_as<std::const_iterator<Iter>, Wrapped> );
|
||||
static_assert( std::same_as<std::const_sentinel<Iter>, Wrapped> );
|
||||
static_assert( std::same_as<std::iter_const_reference_t<Iter>,
|
||||
std::iter_reference_t<Wrapped>> );
|
||||
|
||||
static_assert( std::input_iterator<Iter> == std::input_iterator<Wrapped> );
|
||||
static_assert( std::forward_iterator<Iter> == std::forward_iterator<Wrapped> );
|
||||
static_assert( std::bidirectional_iterator<Iter> == std::bidirectional_iterator<Wrapped> );
|
||||
static_assert( std::random_access_iterator<Iter> == std::random_access_iterator<Wrapped> );
|
||||
}
|
||||
}
|
||||
|
||||
template<class Range, bool Const>
|
||||
void
|
||||
test02()
|
||||
{
|
||||
if constexpr (Const)
|
||||
{
|
||||
static_assert( ranges::constant_range<Range> );
|
||||
static_assert( std::same_as<ranges::const_iterator_t<Range>, ranges::iterator_t<Range>> );
|
||||
static_assert( std::same_as<ranges::const_sentinel_t<Range>, ranges::sentinel_t<Range>> );
|
||||
static_assert( std::same_as<ranges::range_const_reference_t<Range>,
|
||||
ranges::range_reference_t<Range>> );
|
||||
|
||||
static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
|
||||
decltype(ranges::begin(std::declval<Range&>()))> );
|
||||
static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())),
|
||||
decltype(ranges::end(std::declval<Range&>()))> );
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( !ranges::constant_range<Range> );
|
||||
using Wrapped = std::basic_const_iterator<ranges::iterator_t<Range>>;
|
||||
|
||||
static_assert( std::same_as<ranges::const_iterator_t<Range>, Wrapped> );
|
||||
if constexpr (ranges::common_range<Range>)
|
||||
static_assert( std::same_as<ranges::const_sentinel_t<Range>, Wrapped> );
|
||||
static_assert( std::same_as<ranges::range_const_reference_t<Range>,
|
||||
std::iter_reference_t<Wrapped>> );
|
||||
|
||||
static_assert( ranges::input_range<Range> == std::input_iterator<Wrapped> );
|
||||
static_assert( ranges::forward_range<Range> == std::forward_iterator<Wrapped> );
|
||||
static_assert( ranges::bidirectional_range<Range> == std::bidirectional_iterator<Wrapped> );
|
||||
static_assert( ranges::random_access_range<Range> == std::random_access_iterator<Wrapped> );
|
||||
|
||||
if constexpr (ranges::constant_range<const Range&>)
|
||||
{
|
||||
static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())),
|
||||
decltype(ranges::begin(std::declval<const Range&>()))> );
|
||||
static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())),
|
||||
decltype(ranges::end(std::declval<const Range&>()))> );
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert( std::same_as<decltype(ranges::cbegin(std::declval<Range&>())), Wrapped> );
|
||||
if constexpr (ranges::common_range<Range>)
|
||||
static_assert( std::same_as<decltype(ranges::cend(std::declval<Range&>())), Wrapped> );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test03()
|
||||
{
|
||||
static_assert( std::same_as<std::const_sentinel<std::unreachable_sentinel_t>,
|
||||
std::unreachable_sentinel_t> );
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test01<int*, false>();
|
||||
test01<ranges::iterator_t<test_input_range<int>>, false>();
|
||||
test01<ranges::iterator_t<test_forward_range<int>>, false>();
|
||||
test01<ranges::iterator_t<test_bidirectional_range<int>>, false>();
|
||||
test01<ranges::iterator_t<test_random_access_range<int>>, false>();
|
||||
test01<std::array<int, 3>::iterator, false>();
|
||||
test01<std::vector<bool>::iterator, false>();
|
||||
|
||||
test01<const int*, true>();
|
||||
test01<ranges::iterator_t<test_input_range<const int>>, true>();
|
||||
test01<ranges::iterator_t<test_forward_range<const int>>, true>();
|
||||
test01<ranges::iterator_t<test_bidirectional_range<const int>>, true>();
|
||||
test01<ranges::iterator_t<test_random_access_range<const int>>, true>();
|
||||
test01<std::array<const int, 3>::iterator, true>();
|
||||
test01<std::string_view::iterator, true>();
|
||||
test01<std::vector<bool>::const_iterator, true>();
|
||||
|
||||
test02<int[42], false>();
|
||||
test02<test_input_range<int>, false>();
|
||||
test02<test_forward_range<int>, false>();
|
||||
test02<test_bidirectional_range<int>, false>();
|
||||
test02<test_random_access_range<int>, false>();
|
||||
test02<std::array<int, 3>, false>();
|
||||
test02<std::vector<bool>, false>();
|
||||
|
||||
test02<const int[42], true>();
|
||||
test02<test_input_range<const int>, true>();
|
||||
test02<test_forward_range<const int>, true>();
|
||||
test02<test_bidirectional_range<const int>, true>();
|
||||
test02<test_random_access_range<const int>, true>();
|
||||
test02<std::array<const int, 3>, true>();
|
||||
test02<const std::array<int, 3>, true>();
|
||||
test02<std::string_view, true>();
|
||||
test02<const std::vector<bool>, true>();
|
||||
|
||||
test03();
|
||||
}
|
64
libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
Normal file
64
libstdc++-v3/testsuite/std/ranges/adaptors/as_const/1.cc
Normal file
@ -0,0 +1,64 @@
|
||||
// { dg-options "-std=gnu++23" }
|
||||
// { dg-do run { target c++23 } }
|
||||
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_iterators.h>
|
||||
|
||||
#if __cpp_lib_ranges_as_const != 202207L
|
||||
# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in <ranges>"
|
||||
#endif
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
namespace views = std::views;
|
||||
|
||||
constexpr bool
|
||||
test01()
|
||||
{
|
||||
int x[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
auto v = x | views::filter([](int x) { return (x % 2) == 0; }) | views::as_const;
|
||||
|
||||
using ty = decltype(v);
|
||||
static_assert(ranges::constant_range<ty>);
|
||||
static_assert(!ranges::constant_range<decltype(v.base())>);
|
||||
static_assert(std::same_as<ranges::range_reference_t<ty>, const int&>);
|
||||
static_assert(std::same_as<ranges::range_reference_t<decltype(v.base())>, int&>);
|
||||
|
||||
VERIFY( ranges::equal(v, (int[]){2, 4, 6, 8, 10}) );
|
||||
VERIFY( ranges::equal(v | views::reverse, (int[]){10, 8, 6, 4, 2}) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
test02()
|
||||
{
|
||||
std::same_as<ranges::empty_view<const int>> auto v1
|
||||
= views::empty<int> | views::as_const;
|
||||
|
||||
int x[] = {1, 2, 3};
|
||||
std::same_as<ranges::as_const_view<ranges::ref_view<int[3]>>> auto v2
|
||||
= x | views::as_const;
|
||||
std::same_as<ranges::ref_view<const int[3]>> auto v3
|
||||
= std::as_const(x) | views::as_const;
|
||||
std::same_as<ranges::ref_view<const int[3]>> auto v4
|
||||
= std::as_const(x) | views::all | views::as_const;
|
||||
std::same_as<std::span<const int>> auto v5
|
||||
= std::span{x, x+3} | views::as_const;
|
||||
|
||||
std::same_as<ranges::as_const_view<ranges::chunk_view<ranges::ref_view<int[3]>>>> auto v6
|
||||
= x | views::chunk(2) | views::as_const;
|
||||
VERIFY( v6.size() == 2 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
static_assert(test01());
|
||||
static_assert(test02());
|
||||
}
|
@ -24,6 +24,7 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_iterators.h>
|
||||
@ -113,15 +114,15 @@ test06()
|
||||
|
||||
// Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
|
||||
static_assert(!std::same_as<decltype(ranges::begin(v)),
|
||||
decltype(ranges::cbegin(v))>);
|
||||
auto a = ranges::cbegin(v);
|
||||
decltype(std::as_const(v).begin())>);
|
||||
auto a = std::as_const(v).begin();
|
||||
a = ranges::begin(v);
|
||||
|
||||
// Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
|
||||
static_assert(!ranges::common_range<decltype(v)>);
|
||||
static_assert(!std::same_as<decltype(ranges::end(v)),
|
||||
decltype(ranges::cend(v))>);
|
||||
auto b = ranges::cend(v);
|
||||
decltype(std::as_const(v).end())>);
|
||||
auto b = std::as_const(v).end();
|
||||
b = ranges::end(v);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,10 @@
|
||||
# error "Feature-test macro __cpp_lib_ranges_as_rvalue has wrong value in <version>"
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_ranges_as_const != 202207L
|
||||
# error "Feature-test macro __cpp_lib_ranges_as_const has wrong value in <version>"
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_ranges_enumerate != 202302L
|
||||
# error "Feature-test macro __cpp_lib_ranges_enumerate has wrong value in <version>"
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user