Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of array rvalues for ranges::cbegin and its friends #3316

Merged
90 changes: 60 additions & 30 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -2479,17 +2479,31 @@ namespace ranges {
_NODISCARD constexpr auto _As_const_pointer(const _Ty* _Ptr) noexcept {
return _Ptr;
}

template <class _Ty>
using _Begin_adapted = decltype(_RANGES begin(_RANGES _Possibly_const_range(_STD declval<_Ty&>())));
frederick-vs-ja marked this conversation as resolved.
Show resolved Hide resolved

template <class _Ty>
frederick-vs-ja marked this conversation as resolved.
Show resolved Hide resolved
concept _Range_accessible_and_begin_adaptable =
_Should_range_access<_Ty>
&& requires(
_Ty& _Val) { const_iterator<_Begin_adapted<_Ty>>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}; };

template <class _Ty>
using _End_adapted = decltype(_RANGES end(_RANGES _Possibly_const_range(_STD declval<_Ty&>())));

template <class _Ty>
concept _Range_accessible_and_end_adaptable =
_Should_range_access<_Ty>
&& requires(_Ty& _Val) { const_sentinel<_End_adapted<_Ty>>{_RANGES end(_RANGES _Possibly_const_range(_Val))}; };
#endif // _HAS_CXX23

struct _Cbegin_fn {
#if _HAS_CXX23
template <_Should_range_access _Ty,
class _Uty = decltype(_RANGES begin(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(const_iterator<_Uty>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}))
requires requires { const_iterator<_Uty>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}; }
{
return const_iterator<_Uty>{_RANGES begin(_RANGES _Possibly_const_range(_Val))};
template <_Range_accessible_and_begin_adaptable _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept(
noexcept(const_iterator<_Begin_adapted<_Ty>>{_RANGES begin(_RANGES _Possibly_const_range(_Val))})) {
return const_iterator<_Begin_adapted<_Ty>>{_RANGES begin(_RANGES _Possibly_const_range(_Val))};
}
#else // ^^^ C++23 / C++20 vvv
template <class _Ty, class _CTy = _Const_thru_ref<_Ty>>
Expand All @@ -2508,13 +2522,10 @@ namespace ranges {

struct _Cend_fn {
#if _HAS_CXX23
template <_Should_range_access _Ty,
class _Uty = decltype(_RANGES end(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))>
template <_Range_accessible_and_end_adaptable _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(const_sentinel<_Uty>{_RANGES end(_RANGES _Possibly_const_range(_Val))}))
requires requires { const_sentinel<_Uty>{_RANGES end(_RANGES _Possibly_const_range(_Val))}; }
{
return const_sentinel<_Uty>{_RANGES end(_RANGES _Possibly_const_range(_Val))};
noexcept(noexcept(const_sentinel<_End_adapted<_Ty>>{_RANGES end(_RANGES _Possibly_const_range(_Val))})) {
return const_sentinel<_End_adapted<_Ty>>{_RANGES end(_RANGES _Possibly_const_range(_Val))};
}
#else // ^^^ C++23 / C++20 vvv
template <class _Ty, class _CTy = _Const_thru_ref<_Ty>>
Expand Down Expand Up @@ -2670,15 +2681,32 @@ namespace ranges {
_EXPORT_STD inline constexpr _Rend::_Cpo rend;
}

#if _HAS_CXX23
template <class _Ty>
using _Rbegin_adapted = decltype(_RANGES rbegin(_RANGES _Possibly_const_range(_STD declval<_Ty&>())));

template <class _Ty>
concept _Range_accessible_and_rbegin_adaptable =
_Should_range_access<_Ty>
&& requires(
_Ty& _Val) { const_iterator<_Rbegin_adapted<_Ty>>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}; };

template <class _Ty>
using _Rend_adapted = decltype(_RANGES rend(_RANGES _Possibly_const_range(_STD declval<_Ty&>())));

template <class _Ty>
concept _Range_accessible_and_rend_adaptable =
_Should_range_access<_Ty>
&& requires(
_Ty& _Val) { const_sentinel<_Rend_adapted<_Ty>>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}; };
#endif // _HAS_CXX23

struct _Crbegin_fn {
#if _HAS_CXX23
template <_Should_range_access _Ty,
class _Uty = decltype(_RANGES rbegin(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(const_iterator<_Uty>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}))
requires requires { const_iterator<_Uty>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}; }
{
return const_iterator<_Uty>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))};
template <_Range_accessible_and_rbegin_adaptable _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept(
noexcept(const_iterator<_Rbegin_adapted<_Ty>>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))})) {
return const_iterator<_Rbegin_adapted<_Ty>>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))};
}
#else // ^^^ C++23 / C++20 vvv
template <class _Ty, class _CTy = _Const_thru_ref<_Ty>>
Expand All @@ -2697,13 +2725,10 @@ namespace ranges {

struct _Crend_fn {
#if _HAS_CXX23
template <_Should_range_access _Ty,
class _Uty = decltype(_RANGES rend(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))>
template <_Range_accessible_and_rend_adaptable _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(const_sentinel<_Uty>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}))
requires requires { const_sentinel<_Uty>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}; }
{
return const_sentinel<_Uty>{_RANGES rend(_RANGES _Possibly_const_range(_Val))};
noexcept(noexcept(const_sentinel<_Rend_adapted<_Ty>>{_RANGES rend(_RANGES _Possibly_const_range(_Val))})) {
return const_sentinel<_Rend_adapted<_Ty>>{_RANGES rend(_RANGES _Possibly_const_range(_Val))};
}
#else // ^^^ C++23 / C++20 vvv
template <class _Ty, class _CTy = _Const_thru_ref<_Ty>>
Expand Down Expand Up @@ -2922,13 +2947,18 @@ namespace ranges {
_EXPORT_STD inline constexpr _Data::_Cpo data;
}

#if _HAS_CXX23
template <class _Ty>
concept _Range_accessible_and_data_adaptable =
_Should_range_access<_Ty>
&& requires(_Ty& _Val) { _RANGES _As_const_pointer(_RANGES data(_RANGES _Possibly_const_range(_Val))); };
#endif // _HAS_CXX23

struct _Cdata_fn {
#if _HAS_CXX23
template <_Should_range_access _Ty>
template <_Range_accessible_and_data_adaptable _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(_RANGES data(_RANGES _Possibly_const_range(_Val))))
requires requires { _RANGES _As_const_pointer(_RANGES data(_RANGES _Possibly_const_range(_Val))); }
{
noexcept(noexcept(_RANGES data(_RANGES _Possibly_const_range(_Val)))) {
return _RANGES _As_const_pointer(_RANGES data(_RANGES _Possibly_const_range(_Val)));
Comment on lines +2967 to 2968
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change requested: I observe that the noexcept expression differs from the return expression because _As_const_pointer is unconditionally noexcept - this is a reasonable shortcut for throughput.

}
#else // ^^^ C++23 / C++20 vvv
Expand Down
14 changes: 14 additions & 0 deletions tests/std/tests/P0896R4_ranges_range_machinery/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,20 @@ struct initially_incomplete;
extern initially_incomplete array_of_incomplete[42];
STATIC_ASSERT(ranges::size(array_of_incomplete) == 42);
STATIC_ASSERT(!ranges::empty(array_of_incomplete));

// begin, end, rbegin, rend, and data (and their c variations) should reject rvalues of array of incomplete elements
// with substitution failures
STATIC_ASSERT(!CanBegin<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanCBegin<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanEnd<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanCEnd<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanRBegin<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanCRBegin<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanREnd<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanCREnd<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanData<initially_incomplete (&&)[42]>);
STATIC_ASSERT(!CanCData<initially_incomplete (&&)[42]>);

struct initially_incomplete {};
initially_incomplete array_of_incomplete[42];

Expand Down