On Tue, 4 Feb 2025, Patrick Palka wrote:
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk and perhaps
> 14 (after a long while)?
>
> -- >8 --
>
> LWG 4027 effectively makes the const range access CPOs ranges::cfoo behave
> more consistently across C++23 and C++20 (pre-P2278R4) and more
> consistently with the std::cfoo range accessors, as the below testcase
> adjustments demonstrate (which mostly consist of reverting workarounds
> added by r14-3771-gf12e26f3496275 and r13-7186-g0d94c6df183375).
>
> In passing fix PR118083 which reports that the input_range constraint on
> possibly-const-range is missing in our implementation. A consequence of
> this is that the const range access CPOs now consistently require the
> type to be a range, and so in some our of tests we need to introduce
> otherwise unused begin/end members.
... now with the LWG 4027 testcases added (to cbegin.cc, so that it runs
in both C++20 and C++23 mode):
-- >8 --
Subject: [PATCH] libstdc++: Implement LWG 4027 change to possibly-const-range
[PR118083]
PR libstdc++/118083
libstdc++-v3/ChangeLog:
* include/bits/ranges_base.h
(ranges::__access::__possibly_const_range): Adjust logic as per
LWG 4027. Add missing input_range constraint.
* testsuite/std/ranges/access/cbegin.cc (test05): Verify LWG
4027 testcases.
* testsuite/std/ranges/access/cdata.cc: Adjust, simplify and
consolidate some tests after the above.
* testsuite/std/ranges/access/cend.cc: Likewise.
* testsuite/std/ranges/access/crbegin.cc: Likewise.
* testsuite/std/ranges/access/crend.cc: Likewise.
* testsuite/std/ranges/adaptors/join.cc: Likewise.
* testsuite/std/ranges/adaptors/take_while.cc: Likewise.
* testsuite/std/ranges/adaptors/transform.cc: Likewise.
---
libstdc++-v3/include/bits/ranges_base.h | 4 +-
.../testsuite/std/ranges/access/cbegin.cc | 17 ++++++++
.../testsuite/std/ranges/access/cdata.cc | 21 ++++-----
.../testsuite/std/ranges/access/cend.cc | 30 ++-----------
.../testsuite/std/ranges/access/crbegin.cc | 43 +++++--------------
.../testsuite/std/ranges/access/crend.cc | 20 +++++----
.../testsuite/std/ranges/adaptors/join.cc | 8 ++--
.../std/ranges/adaptors/take_while.cc | 2 -
.../std/ranges/adaptors/transform.cc | 4 --
9 files changed, 59 insertions(+), 90 deletions(-)
diff --git a/libstdc++-v3/include/bits/ranges_base.h
b/libstdc++-v3/include/bits/ranges_base.h
index 4dcfbf66d51..28fe64a9e9d 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -642,11 +642,11 @@ namespace ranges
namespace __access
{
#if __glibcxx_ranges_as_const // >= C++23
- template<typename _Range>
+ template<input_range _Range>
constexpr auto&
__possibly_const_range(_Range& __r) noexcept
{
- if constexpr (constant_range<const _Range> && !constant_range<_Range>)
+ if constexpr (input_range<const _Range>)
return const_cast<const _Range&>(__r);
else
return __r;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
index 5423e782428..66b28f41877 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cbegin.cc
@@ -116,10 +116,27 @@ test04()
VERIFY(std::ranges::cbegin(std::move(c)) == std::ranges::begin(c));
}
+void
+test05()
+{
+ // LWG 4027 - possibly-const-range should prefer returning const R&
+ auto r = std::views::single(0)
+ | std::views::transform([](int) { return 0; });
+ using C1 = decltype(std::ranges::cbegin(r));
+ using C1 = decltype(std::cbegin(r));
+
+ [] (auto x) {
+ auto r = std::views::single(x) | std::views::lazy_split(0);
+ static_assert( ! requires { (*std::ranges::cbegin(r)).front() = 42; });
+ static_assert( ! requires { (*std::cbegin(r)).front() = 42; });
+ }(0);
+}
+
int
main()
{
test01();
test03();
test04();
+ test05();
}
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
index 62c347be43d..f474ab7ec99 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
@@ -34,20 +34,21 @@ test01()
{
int i = 0;
int j = 0;
+
+#if __cpp_lib_ranges_as_const
+ // These overloads mean that range<R> and range<const R> are satisfied.
+ const int* begin() const { throw; }
+ const int* end() const { throw; }
+#endif
+
int* data() { return &j; }
const R* data() const noexcept { return nullptr; }
};
static_assert( has_cdata<R&> );
static_assert( has_cdata<const R&> );
R r;
-#if ! __cpp_lib_ranges_as_const
VERIFY( std::ranges::cdata(r) == (R*)nullptr );
static_assert( noexcept(std::ranges::cdata(r)) );
-#else
- // constant_range<const R> is not satisfied, so cdata(r) == data(r).
- VERIFY( std::ranges::cdata(r) == &r.j );
- static_assert( ! noexcept(std::ranges::cdata(r)) );
-#endif
const R& c = r;
VERIFY( std::ranges::cdata(c) == (R*)nullptr );
static_assert( noexcept(std::ranges::cdata(c)) );
@@ -58,11 +59,11 @@ test01()
struct R2
{
+#if __cpp_lib_ranges_as_const
// These overloads mean that range<R2> and range<const R2> are satisfied.
- int* begin();
- int* end();
- const int* begin() const;
- const int* end() const;
+ const int* begin() const { throw; }
+ const int* end() const { throw; }
+#endif
int i = 0;
int j = 0;
diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
index 6194fe7d866..6903c4558a1 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
@@ -52,15 +52,6 @@ struct R
friend const int* end(const R&& r) noexcept { return r.a + 3; }
};
-#if __cpp_lib_ranges_as_const
-struct R2 : R
-{
- // This overload means constant_range<const R2> will be satisfied:
- friend const int* begin(const R2&) noexcept;
- friend const int* end(const R2& r2) noexcept { return r2.a + 2; }
-};
-#endif
-
struct RV // view on an R
{
R& r;
@@ -79,26 +70,11 @@ test03()
{
R r;
const R& c = r;
-#if ! __cpp_lib_ranges_as_const
VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
-#else
- // constant_range<const R> is not satisfied, so cend(r) == end(r) instead.
- VERIFY( std::ranges::cend(r) == std::ranges::end(r) );
- R2 r2;
- const R& c2 = r2;
- // But constant_range<const R2> is satisfied, so cend(r2) == end(c2).
- VERIFY( std::ranges::cend(r2) == std::ranges::end(c2) );
- VERIFY( std::ranges::cend(r2) == std::ranges::end((const R&)c2) );
-#endif
VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
RV v{r};
-#if ! __cpp_lib_ranges_as_const
VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) );
-#else
- // constant_range<RV> is already satisfied, so cend(v) == end(r) instead.
- VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(r) );
-#endif
const RV cv{r};
VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) );
@@ -107,7 +83,7 @@ test03()
struct RR
{
short s = 0;
- long l = 0;
+ short l = 0;
int a[4] = { 0, 1, 2, 3 };
const void* begin() const; // return type not an iterator
@@ -115,8 +91,8 @@ struct RR
friend int* end(RR&) { throw 1; }
short* end() noexcept { return &s; }
- friend const long* begin(const RR&) noexcept;
- const long* end() const { return &l; }
+ friend const short* begin(const RR&) noexcept;
+ const short* end() const { return &l; }
friend int* begin(RR&&) noexcept;
friend int* end(RR&& r) { return r.a + 1; }
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
index 9a07f0b3874..c283ee4e33c 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
@@ -28,6 +28,11 @@ struct R1
int i = 0;
int j = 0;
+#if __cpp_lib_ranges_as_const
+ const int *begin() const;
+ const int *end() const;
+#endif
+
const int* rbegin() const { return &i; }
friend const int* rbegin(const R1&& r) { return &r.j; }
};
@@ -36,6 +41,11 @@ struct R1V // view on an R1
{
R1& r;
+#if __cpp_lib_ranges_as_const
+ const int *begin() const;
+ const int *end() const;
+#endif
+
friend const long* rbegin(R1V&) { return nullptr; }
friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
};
@@ -43,26 +53,6 @@ struct R1V // view on an R1
// Allow ranges::end to work with R1V&&
template<> constexpr bool std::ranges::enable_borrowed_range<R1V> = true;
-#if __cpp_lib_ranges_as_const
-struct R1VC // view on an R1
-{
- R1& r;
-
- friend const long* rbegin(R1VC&); // this is not defined
- friend const int* rbegin(const R1VC& rv) noexcept { return rv.r.rbegin(); }
-
- // The following ensure that the following are satisfied:
- // constant_range<const R1VC> && ! constant_range<R1VC>
- friend int* begin(R1VC&);
- friend int* end(R1VC&);
- friend const int* begin(const R1VC&);
- friend const int* end(const R1VC&);
-};
-
-// Allow ranges::end to work with R1VC&&
-template<> constexpr bool std::ranges::enable_borrowed_range<R1VC> = true;
-#endif
-
void
test01()
{
@@ -72,21 +62,8 @@ test01()
VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
R1V v{r};
-#if ! __cpp_lib_ranges_as_const
VERIFY( std::ranges::crbegin(v) == std::ranges::rbegin(c) );
VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) );
-#else
- // constant_range<const R1V> is not satisfied, so crbegin(v) == rbegin(v).
- VERIFY( std::ranges::crbegin(v) == (long*)nullptr );
- VERIFY( std::ranges::crbegin(std::move(v)) == (long*)nullptr );
- R1VC v2{r};
- // But constant_range<const R1VC> is satisfied:
- VERIFY( std::ranges::crbegin(v2) == std::ranges::rbegin(c) );
- VERIFY( std::ranges::crbegin(std::move(v2)) == std::ranges::rbegin(c) );
- const R1VC cv2{r};
- VERIFY( std::ranges::crbegin(cv2) == std::ranges::rbegin(c) );
- VERIFY( std::ranges::crbegin(std::move(cv2)) == std::ranges::rbegin(c) );
-#endif
const R1V cv{r};
VERIFY( std::ranges::crbegin(cv) == std::ranges::rbegin(c) );
diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
index 6f7dce28200..d4530e530e1 100644
--- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
+++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
@@ -28,6 +28,11 @@ struct R1
int i = 0;
int j = 0;
+#if __cpp_lib_ranges_as_const
+ const int *begin() const;
+ const int *end() const;
+#endif
+
constexpr const int* rbegin() const { return &i; }
constexpr const int* rend() const { return &i + 1; }
friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
@@ -78,6 +83,11 @@ struct R3
{
int i = 0;
+#if __cpp_lib_ranges_as_const
+ const int *begin() const;
+ const int *end() const;
+#endif
+
const int* rbegin() const noexcept { return &i + 1; }
const long* rend() const noexcept { return nullptr; } // not a sentinel for
rbegin()
@@ -89,9 +99,11 @@ struct R4
{
int i = 0;
+#if __cpp_lib_ranges_as_const
// These members mean that range<R4> and range<const R4> are satisfied.
const short* begin() const { return 0; }
const short* end() const { return 0; }
+#endif
const int* rbegin() const noexcept { return &i + 1; }
const long* rend() const noexcept { return nullptr; } // not a sentinel for
rbegin()
@@ -105,16 +117,8 @@ test03()
{
R3 r;
const R3& c = r;
-#if ! __cpp_lib_ranges_as_const
VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
static_assert( !noexcept(std::ranges::crend(r)) );
-#else
- // constant_range<const R3> is not satisfied, so crend(r) is equivalent
- // to const_sentinel{rend(r)}, which is ill-formed because range<R3>
- // is not satisfied.
- static_assert( not std::ranges::range<R3> );
- static_assert( not std::ranges::range<const R3> );
-#endif
VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
static_assert( !noexcept(std::ranges::crend(c)) );
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index 9e1c526b4ca..2861115c22a 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -113,15 +113,15 @@ test06()
// Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
static_assert(!std::same_as<decltype(ranges::begin(v)),
- decltype(std::as_const(v).begin())>);
- auto a = std::as_const(v).begin();
+ decltype(ranges::cbegin(v))>);
+ auto a = std::cbegin(v);
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(std::as_const(v).end())>);
- auto b = std::as_const(v).end();
+ decltype(ranges::cend(v))>);
+ auto b = ranges::cend(v);
b = ranges::end(v);
}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
index 38757d27d48..f09919bedc4 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -63,10 +63,8 @@ test03()
// Verify that _Sentinel<false> is implicitly convertible to _Sentinel<true>.
static_assert(!ranges::common_range<decltype(v)>);
-#if ! __cpp_lib_ranges_as_const
static_assert(!std::same_as<decltype(ranges::end(v)),
decltype(ranges::cend(v))>);
-#endif
auto b = ranges::cend(v);
b = ranges::end(v);
}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index 934d2f65dcf..1788db1ce8d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -107,20 +107,16 @@ test05()
auto r = ranges::subrange{i, std::default_sentinel};
auto v = r | views::transform(std::negate{});
-#if ! __cpp_lib_ranges_as_const
// Verify that _Iterator<false> is implicitly convertible to _Iterator<true>.
static_assert(!std::same_as<decltype(ranges::begin(v)),
decltype(ranges::cbegin(v))>);
-#endif
auto a = ranges::cbegin(v);
a = ranges::begin(v);
-#if ! __cpp_lib_ranges_as_const
// 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))>);
-#endif
auto b = ranges::cend(v);
b = ranges::end(v);
}
--
2.48.1.220.gbc204b7427
>
> PR libstdc++/118083
>
> libstdc++-v3/ChangeLog:
>
> * include/bits/ranges_base.h
> (ranges::__access::__possibly_const_range): Adjust logic as per
> LWG 4027. Add missing input_range constraint.
> * testsuite/std/ranges/access/cdata.cc: Adjust, simplify and
> consolidate some tests after the above.
> * testsuite/std/ranges/access/cend.cc: Likewise.
> * testsuite/std/ranges/access/crbegin.cc: Likewise.
> * testsuite/std/ranges/access/crend.cc: Likewise.
> * testsuite/std/ranges/adaptors/join.cc: Likewise.
> * testsuite/std/ranges/adaptors/take_while.cc: Likewise.
> * testsuite/std/ranges/adaptors/transform.cc: Likewise.
> ---
> libstdc++-v3/include/bits/ranges_base.h | 4 +-
> .../testsuite/std/ranges/access/cdata.cc | 21 ++++-----
> .../testsuite/std/ranges/access/cend.cc | 30 ++-----------
> .../testsuite/std/ranges/access/crbegin.cc | 43 +++++--------------
> .../testsuite/std/ranges/access/crend.cc | 20 +++++----
> .../testsuite/std/ranges/adaptors/join.cc | 8 ++--
> .../std/ranges/adaptors/take_while.cc | 2 -
> .../std/ranges/adaptors/transform.cc | 4 --
> 8 files changed, 42 insertions(+), 90 deletions(-)
>
> diff --git a/libstdc++-v3/include/bits/ranges_base.h
> b/libstdc++-v3/include/bits/ranges_base.h
> index 4dcfbf66d51..28fe64a9e9d 100644
> --- a/libstdc++-v3/include/bits/ranges_base.h
> +++ b/libstdc++-v3/include/bits/ranges_base.h
> @@ -642,11 +642,11 @@ namespace ranges
> namespace __access
> {
> #if __glibcxx_ranges_as_const // >= C++23
> - template<typename _Range>
> + template<input_range _Range>
> constexpr auto&
> __possibly_const_range(_Range& __r) noexcept
> {
> - if constexpr (constant_range<const _Range> && !constant_range<_Range>)
> + if constexpr (input_range<const _Range>)
> return const_cast<const _Range&>(__r);
> else
> return __r;
> diff --git a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
> b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
> index 62c347be43d..f474ab7ec99 100644
> --- a/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/access/cdata.cc
> @@ -34,20 +34,21 @@ test01()
> {
> int i = 0;
> int j = 0;
> +
> +#if __cpp_lib_ranges_as_const
> + // These overloads mean that range<R> and range<const R> are satisfied.
> + const int* begin() const { throw; }
> + const int* end() const { throw; }
> +#endif
> +
> int* data() { return &j; }
> const R* data() const noexcept { return nullptr; }
> };
> static_assert( has_cdata<R&> );
> static_assert( has_cdata<const R&> );
> R r;
> -#if ! __cpp_lib_ranges_as_const
> VERIFY( std::ranges::cdata(r) == (R*)nullptr );
> static_assert( noexcept(std::ranges::cdata(r)) );
> -#else
> - // constant_range<const R> is not satisfied, so cdata(r) == data(r).
> - VERIFY( std::ranges::cdata(r) == &r.j );
> - static_assert( ! noexcept(std::ranges::cdata(r)) );
> -#endif
> const R& c = r;
> VERIFY( std::ranges::cdata(c) == (R*)nullptr );
> static_assert( noexcept(std::ranges::cdata(c)) );
> @@ -58,11 +59,11 @@ test01()
>
> struct R2
> {
> +#if __cpp_lib_ranges_as_const
> // These overloads mean that range<R2> and range<const R2> are satisfied.
> - int* begin();
> - int* end();
> - const int* begin() const;
> - const int* end() const;
> + const int* begin() const { throw; }
> + const int* end() const { throw; }
> +#endif
>
> int i = 0;
> int j = 0;
> diff --git a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
> b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
> index 6194fe7d866..6903c4558a1 100644
> --- a/libstdc++-v3/testsuite/std/ranges/access/cend.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/access/cend.cc
> @@ -52,15 +52,6 @@ struct R
> friend const int* end(const R&& r) noexcept { return r.a + 3; }
> };
>
> -#if __cpp_lib_ranges_as_const
> -struct R2 : R
> -{
> - // This overload means constant_range<const R2> will be satisfied:
> - friend const int* begin(const R2&) noexcept;
> - friend const int* end(const R2& r2) noexcept { return r2.a + 2; }
> -};
> -#endif
> -
> struct RV // view on an R
> {
> R& r;
> @@ -79,26 +70,11 @@ test03()
> {
> R r;
> const R& c = r;
> -#if ! __cpp_lib_ranges_as_const
> VERIFY( std::ranges::cend(r) == std::ranges::end(c) );
> -#else
> - // constant_range<const R> is not satisfied, so cend(r) == end(r) instead.
> - VERIFY( std::ranges::cend(r) == std::ranges::end(r) );
> - R2 r2;
> - const R& c2 = r2;
> - // But constant_range<const R2> is satisfied, so cend(r2) == end(c2).
> - VERIFY( std::ranges::cend(r2) == std::ranges::end(c2) );
> - VERIFY( std::ranges::cend(r2) == std::ranges::end((const R&)c2) );
> -#endif
> VERIFY( std::ranges::cend(c) == std::ranges::end(c) );
>
> RV v{r};
> -#if ! __cpp_lib_ranges_as_const
> VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(c) );
> -#else
> - // constant_range<RV> is already satisfied, so cend(v) == end(r) instead.
> - VERIFY( std::ranges::cend(std::move(v)) == std::ranges::end(r) );
> -#endif
>
> const RV cv{r};
> VERIFY( std::ranges::cend(std::move(cv)) == std::ranges::end(c) );
> @@ -107,7 +83,7 @@ test03()
> struct RR
> {
> short s = 0;
> - long l = 0;
> + short l = 0;
> int a[4] = { 0, 1, 2, 3 };
>
> const void* begin() const; // return type not an iterator
> @@ -115,8 +91,8 @@ struct RR
> friend int* end(RR&) { throw 1; }
> short* end() noexcept { return &s; }
>
> - friend const long* begin(const RR&) noexcept;
> - const long* end() const { return &l; }
> + friend const short* begin(const RR&) noexcept;
> + const short* end() const { return &l; }
>
> friend int* begin(RR&&) noexcept;
> friend int* end(RR&& r) { return r.a + 1; }
> diff --git a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
> b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
> index 9a07f0b3874..c283ee4e33c 100644
> --- a/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/access/crbegin.cc
> @@ -28,6 +28,11 @@ struct R1
> int i = 0;
> int j = 0;
>
> +#if __cpp_lib_ranges_as_const
> + const int *begin() const;
> + const int *end() const;
> +#endif
> +
> const int* rbegin() const { return &i; }
> friend const int* rbegin(const R1&& r) { return &r.j; }
> };
> @@ -36,6 +41,11 @@ struct R1V // view on an R1
> {
> R1& r;
>
> +#if __cpp_lib_ranges_as_const
> + const int *begin() const;
> + const int *end() const;
> +#endif
> +
> friend const long* rbegin(R1V&) { return nullptr; }
> friend const int* rbegin(const R1V& rv) noexcept { return rv.r.rbegin(); }
> };
> @@ -43,26 +53,6 @@ struct R1V // view on an R1
> // Allow ranges::end to work with R1V&&
> template<> constexpr bool std::ranges::enable_borrowed_range<R1V> = true;
>
> -#if __cpp_lib_ranges_as_const
> -struct R1VC // view on an R1
> -{
> - R1& r;
> -
> - friend const long* rbegin(R1VC&); // this is not defined
> - friend const int* rbegin(const R1VC& rv) noexcept { return rv.r.rbegin(); }
> -
> - // The following ensure that the following are satisfied:
> - // constant_range<const R1VC> && ! constant_range<R1VC>
> - friend int* begin(R1VC&);
> - friend int* end(R1VC&);
> - friend const int* begin(const R1VC&);
> - friend const int* end(const R1VC&);
> -};
> -
> -// Allow ranges::end to work with R1VC&&
> -template<> constexpr bool std::ranges::enable_borrowed_range<R1VC> = true;
> -#endif
> -
> void
> test01()
> {
> @@ -72,21 +62,8 @@ test01()
> VERIFY( std::ranges::crbegin(c) == std::ranges::rbegin(c) );
>
> R1V v{r};
> -#if ! __cpp_lib_ranges_as_const
> VERIFY( std::ranges::crbegin(v) == std::ranges::rbegin(c) );
> VERIFY( std::ranges::crbegin(std::move(v)) == std::ranges::rbegin(c) );
> -#else
> - // constant_range<const R1V> is not satisfied, so crbegin(v) == rbegin(v).
> - VERIFY( std::ranges::crbegin(v) == (long*)nullptr );
> - VERIFY( std::ranges::crbegin(std::move(v)) == (long*)nullptr );
> - R1VC v2{r};
> - // But constant_range<const R1VC> is satisfied:
> - VERIFY( std::ranges::crbegin(v2) == std::ranges::rbegin(c) );
> - VERIFY( std::ranges::crbegin(std::move(v2)) == std::ranges::rbegin(c) );
> - const R1VC cv2{r};
> - VERIFY( std::ranges::crbegin(cv2) == std::ranges::rbegin(c) );
> - VERIFY( std::ranges::crbegin(std::move(cv2)) == std::ranges::rbegin(c) );
> -#endif
>
> const R1V cv{r};
> VERIFY( std::ranges::crbegin(cv) == std::ranges::rbegin(c) );
> diff --git a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
> b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
> index 6f7dce28200..d4530e530e1 100644
> --- a/libstdc++-v3/testsuite/std/ranges/access/crend.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/access/crend.cc
> @@ -28,6 +28,11 @@ struct R1
> int i = 0;
> int j = 0;
>
> +#if __cpp_lib_ranges_as_const
> + const int *begin() const;
> + const int *end() const;
> +#endif
> +
> constexpr const int* rbegin() const { return &i; }
> constexpr const int* rend() const { return &i + 1; }
> friend constexpr const int* rbegin(const R1&& r) { return &r.j; }
> @@ -78,6 +83,11 @@ struct R3
> {
> int i = 0;
>
> +#if __cpp_lib_ranges_as_const
> + const int *begin() const;
> + const int *end() const;
> +#endif
> +
> const int* rbegin() const noexcept { return &i + 1; }
> const long* rend() const noexcept { return nullptr; } // not a sentinel
> for rbegin()
>
> @@ -89,9 +99,11 @@ struct R4
> {
> int i = 0;
>
> +#if __cpp_lib_ranges_as_const
> // These members mean that range<R4> and range<const R4> are satisfied.
> const short* begin() const { return 0; }
> const short* end() const { return 0; }
> +#endif
>
> const int* rbegin() const noexcept { return &i + 1; }
> const long* rend() const noexcept { return nullptr; } // not a sentinel
> for rbegin()
> @@ -105,16 +117,8 @@ test03()
> {
> R3 r;
> const R3& c = r;
> -#if ! __cpp_lib_ranges_as_const
> VERIFY( std::ranges::crend(r) == std::ranges::rend(c) );
> static_assert( !noexcept(std::ranges::crend(r)) );
> -#else
> - // constant_range<const R3> is not satisfied, so crend(r) is equivalent
> - // to const_sentinel{rend(r)}, which is ill-formed because range<R3>
> - // is not satisfied.
> - static_assert( not std::ranges::range<R3> );
> - static_assert( not std::ranges::range<const R3> );
> -#endif
> VERIFY( std::ranges::crend(c) == std::ranges::rend(c) );
> static_assert( !noexcept(std::ranges::crend(c)) );
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> index 9e1c526b4ca..2861115c22a 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
> @@ -113,15 +113,15 @@ test06()
>
> // Verify that _Iterator<false> is implicitly convertible to
> _Iterator<true>.
> static_assert(!std::same_as<decltype(ranges::begin(v)),
> - decltype(std::as_const(v).begin())>);
> - auto a = std::as_const(v).begin();
> + decltype(ranges::cbegin(v))>);
> + auto a = std::cbegin(v);
> 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(std::as_const(v).end())>);
> - auto b = std::as_const(v).end();
> + decltype(ranges::cend(v))>);
> + auto b = ranges::cend(v);
> b = ranges::end(v);
> }
>
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
> index 38757d27d48..f09919bedc4 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
> @@ -63,10 +63,8 @@ test03()
>
> // Verify that _Sentinel<false> is implicitly convertible to
> _Sentinel<true>.
> static_assert(!ranges::common_range<decltype(v)>);
> -#if ! __cpp_lib_ranges_as_const
> static_assert(!std::same_as<decltype(ranges::end(v)),
> decltype(ranges::cend(v))>);
> -#endif
> auto b = ranges::cend(v);
> b = ranges::end(v);
> }
> diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> index 934d2f65dcf..1788db1ce8d 100644
> --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
> @@ -107,20 +107,16 @@ test05()
> auto r = ranges::subrange{i, std::default_sentinel};
> auto v = r | views::transform(std::negate{});
>
> -#if ! __cpp_lib_ranges_as_const
> // Verify that _Iterator<false> is implicitly convertible to
> _Iterator<true>.
> static_assert(!std::same_as<decltype(ranges::begin(v)),
> decltype(ranges::cbegin(v))>);
> -#endif
> auto a = ranges::cbegin(v);
> a = ranges::begin(v);
>
> -#if ! __cpp_lib_ranges_as_const
> // 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))>);
> -#endif
> auto b = ranges::cend(v);
> b = ranges::end(v);
> }
> --
> 2.48.1.220.gbc204b7427
>
>