@@ -925,8 +925,11 @@ namespace ranges
struct default_sentinel_t { };
inline constexpr default_sentinel_t default_sentinel{};
- namespace __detail
+ // This is the namespace for [range.access] CPOs.
+ namespace ranges::__cust_access
{
+ using std::__detail::__class_or_enum;
+
template<typename _Tp>
constexpr decay_t<_Tp>
__decay_copy(_Tp&& __t)
@@ -936,9 +939,11 @@ namespace ranges
template<typename _Tp>
concept __member_begin = requires(_Tp& __t)
{
- { __detail::__decay_copy(__t.begin()) } -> input_or_output_iterator;
+ { __cust_access::__decay_copy(__t.begin()) }
+ -> input_or_output_iterator;
};
+ // Poison pills so that unqualified lookup doesn't find std::begin.
void begin(auto&) = delete;
void begin(const auto&) = delete;
@@ -946,7 +951,8 @@ namespace ranges
concept __adl_begin = __class_or_enum<remove_reference_t<_Tp>>
&& requires(_Tp& __t)
{
- { __detail::__decay_copy(begin(__t)) } -> input_or_output_iterator;
+ { __cust_access::__decay_copy(begin(__t)) }
+ -> input_or_output_iterator;
};
// Simplified version of std::ranges::begin that only supports lvalues,
@@ -954,24 +960,23 @@ namespace ranges
template<typename _Tp>
requires is_array_v<_Tp> || __member_begin<_Tp&> || __adl_begin<_Tp&>
auto
- __ranges_begin(_Tp& __t)
+ __begin(_Tp& __t)
{
if constexpr (is_array_v<_Tp>)
- {
- static_assert(sizeof(remove_all_extents_t<_Tp>) != 0,
- "not array of incomplete type");
- return __t + 0;
- }
+ return __t + 0;
else if constexpr (__member_begin<_Tp&>)
return __t.begin();
else
return begin(__t);
}
+ } // namespace ranges::__cust_access
+ namespace __detail
+ {
// Implementation of std::ranges::iterator_t, without using ranges::begin.
template<typename _Tp>
using __range_iter_t
- = decltype(__detail::__ranges_begin(std::declval<_Tp&>()));
+ = decltype(ranges::__cust_access::__begin(std::declval<_Tp&>()));
} // namespace __detail
@@ -89,10 +89,6 @@ namespace ranges
namespace __cust_access
{
using std::ranges::__detail::__maybe_borrowed_range;
- using std::__detail::__class_or_enum;
- using std::__detail::__decay_copy;
- using std::__detail::__member_begin;
- using std::__detail::__adl_begin;
struct _Begin
{
@@ -114,13 +110,11 @@ namespace ranges
requires is_array_v<remove_reference_t<_Tp>> || __member_begin<_Tp>
|| __adl_begin<_Tp>
constexpr auto
- operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
+ operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
{
if constexpr (is_array_v<remove_reference_t<_Tp>>)
{
static_assert(is_lvalue_reference_v<_Tp>);
- using _Up = remove_all_extents_t<remove_reference_t<_Tp>>;
- static_assert(sizeof(_Up) != 0, "not array of incomplete type");
return __t + 0;
}
else if constexpr (__member_begin<_Tp>)
@@ -137,6 +131,7 @@ namespace ranges
-> sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
};
+ // Poison pills so that unqualified lookup doesn't find std::end.
void end(auto&) = delete;
void end(const auto&) = delete;
@@ -165,10 +160,10 @@ namespace ranges
public:
template<__maybe_borrowed_range _Tp>
- requires is_bounded_array_v<remove_reference_t<_Tp>> || __member_end<_Tp>
- || __adl_end<_Tp>
+ requires is_bounded_array_v<remove_reference_t<_Tp>>
+ || __member_end<_Tp> || __adl_end<_Tp>
constexpr auto
- operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
+ operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
{
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
{
@@ -182,12 +177,15 @@ namespace ranges
}
};
- template<typename _Tp>
+ // If _To is an lvalue-reference, return const _Tp&, otherwise const _Tp&&.
+ template<typename _To, typename _Tp>
constexpr decltype(auto)
- __as_const(_Tp&& __t) noexcept
+ __as_const(_Tp& __t) noexcept
{
- if constexpr (is_lvalue_reference_v<_Tp>)
- return static_cast<const remove_reference_t<_Tp>&>(__t);
+ static_assert(std::is_same_v<_To&, _Tp&>);
+
+ if constexpr (is_lvalue_reference_v<_To>)
+ return const_cast<const _Tp&>(__t);
else
return static_cast<const _Tp&&>(__t);
}
@@ -197,10 +195,10 @@ namespace ranges
template<typename _Tp>
constexpr auto
operator()(_Tp&& __e) const
- noexcept(noexcept(_Begin{}(__cust_access::__as_const((_Tp&&)__e))))
- requires requires { _Begin{}(__cust_access::__as_const((_Tp&&)__e)); }
+ noexcept(noexcept(_Begin{}(__cust_access::__as_const<_Tp>(__e))))
+ requires requires { _Begin{}(__cust_access::__as_const<_Tp>(__e)); }
{
- return _Begin{}(__cust_access::__as_const(std::forward<_Tp>(__e)));
+ return _Begin{}(__cust_access::__as_const<_Tp>(__e));
}
};
@@ -209,10 +207,10 @@ namespace ranges
template<typename _Tp>
constexpr auto
operator()(_Tp&& __e) const
- noexcept(noexcept(_End{}(__cust_access::__as_const((_Tp&&)__e))))
- requires requires { _End{}(__cust_access::__as_const((_Tp&&)__e)); }
+ noexcept(noexcept(_End{}(__cust_access::__as_const<_Tp>(__e))))
+ requires requires { _End{}(__cust_access::__as_const<_Tp>(__e)); }
{
- return _End{}(__cust_access::__as_const(std::forward<_Tp>(__e)));
+ return _End{}(__cust_access::__as_const<_Tp>(__e));
}
};
@@ -268,7 +266,7 @@ namespace ranges
requires __member_rbegin<_Tp> || __adl_rbegin<_Tp> || __reversable<_Tp>
constexpr auto
operator()(_Tp&& __t) const
- noexcept(_S_noexcept<_Tp>())
+ noexcept(_S_noexcept<_Tp&>())
{
if constexpr (__member_rbegin<_Tp>)
return __t.rbegin();
@@ -326,7 +324,7 @@ namespace ranges
requires __member_rend<_Tp> || __adl_rend<_Tp> || __reversable<_Tp>
constexpr auto
operator()(_Tp&& __t) const
- noexcept(_S_noexcept<_Tp>())
+ noexcept(_S_noexcept<_Tp&>())
{
if constexpr (__member_rend<_Tp>)
return __t.rend();
@@ -342,10 +340,10 @@ namespace ranges
template<typename _Tp>
constexpr auto
operator()(_Tp&& __e) const
- noexcept(noexcept(_RBegin{}(__cust_access::__as_const((_Tp&&)__e))))
- requires requires { _RBegin{}(__cust_access::__as_const((_Tp&&)__e)); }
+ noexcept(noexcept(_RBegin{}(__cust_access::__as_const<_Tp>(__e))))
+ requires requires { _RBegin{}(__cust_access::__as_const<_Tp>(__e)); }
{
- return _RBegin{}(__cust_access::__as_const(std::forward<_Tp>(__e)));
+ return _RBegin{}(__cust_access::__as_const<_Tp>(__e));
}
};
@@ -354,19 +352,18 @@ namespace ranges
template<typename _Tp>
constexpr auto
operator()(_Tp&& __e) const
- noexcept(noexcept(_REnd{}(__cust_access::__as_const((_Tp&&)__e))))
- requires requires { _REnd{}(__cust_access::__as_const((_Tp&&)__e)); }
+ noexcept(noexcept(_REnd{}(__cust_access::__as_const<_Tp>(__e))))
+ requires requires { _REnd{}(__cust_access::__as_const<_Tp>(__e)); }
{
- return _REnd{}(__cust_access::__as_const(std::forward<_Tp>(__e)));
+ return _REnd{}(__cust_access::__as_const<_Tp>(__e));
}
};
template<typename _Tp>
concept __member_size = !disable_sized_range<remove_cvref_t<_Tp>>
- && requires(_Tp&& __t)
+ && requires(_Tp& __t)
{
- { __decay_copy(std::forward<_Tp>(__t).size()) }
- -> __detail::__is_integer_like;
+ { __decay_copy(__t.size()) } -> __detail::__is_integer_like;
};
void size(auto&) = delete;
@@ -375,19 +372,19 @@ namespace ranges
template<typename _Tp>
concept __adl_size = __class_or_enum<remove_reference_t<_Tp>>
&& !disable_sized_range<remove_cvref_t<_Tp>>
- && requires(_Tp&& __t)
+ && requires(_Tp& __t)
{
- { __decay_copy(size(std::forward<_Tp>(__t))) }
- -> __detail::__is_integer_like;
+ { __decay_copy(size(__t)) } -> __detail::__is_integer_like;
};
template<typename _Tp>
- concept __sentinel_size = requires(_Tp&& __t)
+ concept __sentinel_size = requires(_Tp& __t)
{
- { _Begin{}(std::forward<_Tp>(__t)) } -> forward_iterator;
+ { _Begin{}(__t) } -> forward_iterator;
- { _End{}(std::forward<_Tp>(__t)) }
- -> sized_sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
+ { _End{}(__t) } -> sized_sentinel_for<decltype(_Begin{}(__t))>;
+
+ __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
};
struct _Size
@@ -400,12 +397,12 @@ namespace ranges
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
return true;
else if constexpr (__member_size<_Tp>)
- return noexcept(__decay_copy(std::declval<_Tp>().size()));
+ return noexcept(__decay_copy(std::declval<_Tp&>().size()));
else if constexpr (__adl_size<_Tp>)
- return noexcept(__decay_copy(size(std::declval<_Tp>())));
+ return noexcept(__decay_copy(size(std::declval<_Tp&>())));
else if constexpr (__sentinel_size<_Tp>)
- return noexcept(_End{}(std::declval<_Tp>())
- - _Begin{}(std::declval<_Tp>()));
+ return noexcept(_End{}(std::declval<_Tp&>())
+ - _Begin{}(std::declval<_Tp&>()));
}
public:
@@ -413,39 +410,30 @@ namespace ranges
requires is_bounded_array_v<remove_reference_t<_Tp>>
|| __member_size<_Tp> || __adl_size<_Tp> || __sentinel_size<_Tp>
constexpr auto
- operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
+ operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
{
if constexpr (is_bounded_array_v<remove_reference_t<_Tp>>)
- {
- return extent_v<remove_reference_t<_Tp>>;
- }
+ return extent_v<remove_reference_t<_Tp>>;
else if constexpr (__member_size<_Tp>)
- return std::forward<_Tp>(__e).size();
+ return __t.size();
else if constexpr (__adl_size<_Tp>)
- return size(std::forward<_Tp>(__e));
+ return size(__t);
else if constexpr (__sentinel_size<_Tp>)
- return __detail::__to_unsigned_like(
- _End{}(std::forward<_Tp>(__e))
- - _Begin{}(std::forward<_Tp>(__e)));
+ return __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
}
};
struct _SSize
{
template<typename _Tp>
- requires requires (_Tp&& __e)
- {
- _Begin{}(std::forward<_Tp>(__e));
- _Size{}(std::forward<_Tp>(__e));
- }
+ requires requires (_Tp& __t) { _Size{}(__t); }
constexpr auto
- operator()(_Tp&& __e) const
- noexcept(noexcept(_Size{}(std::forward<_Tp>(__e))))
+ operator()(_Tp&& __t) const noexcept(noexcept(_Size{}(__t)))
{
- using __iter_type = decltype(_Begin{}(std::forward<_Tp>(__e)));
+ using __iter_type = decltype(_Begin{}(__t));
using __diff_type = iter_difference_t<__iter_type>;
using __gnu_cxx::__int_traits;
- auto __size = _Size{}(std::forward<_Tp>(__e));
+ auto __size = _Size{}(__t);
if constexpr (integral<__diff_type>)
{
if constexpr (__int_traits<__diff_type>::__digits
@@ -457,19 +445,17 @@ namespace ranges
};
template<typename _Tp>
- concept __member_empty = requires(_Tp&& __t)
- { bool(std::forward<_Tp>(__t).empty()); };
+ concept __member_empty = requires(_Tp& __t) { bool(__t.empty()); };
template<typename _Tp>
- concept __size0_empty = requires(_Tp&& __t)
- { _Size{}(std::forward<_Tp>(__t)) == 0; };
+ concept __size0_empty = requires(_Tp& __t) { _Size{}(__t) == 0; };
template<typename _Tp>
- concept __eq_iter_empty = requires(_Tp&& __t)
+ concept __eq_iter_empty = requires(_Tp& __t)
{
- { _Begin{}(std::forward<_Tp>(__t)) } -> forward_iterator;
- bool(_Begin{}(std::forward<_Tp>(__t))
- == _End{}(std::forward<_Tp>(__t)));
+ { _Begin{}(__t) } -> forward_iterator;
+
+ bool(_Begin{}(__t) == _End{}(__t));
};
struct _Empty
@@ -480,28 +466,27 @@ namespace ranges
_S_noexcept()
{
if constexpr (__member_empty<_Tp>)
- return noexcept(std::declval<_Tp>().empty());
+ return noexcept(std::declval<_Tp&>().empty());
else if constexpr (__size0_empty<_Tp>)
- return noexcept(_Size{}(std::declval<_Tp>()) == 0);
+ return noexcept(_Size{}(std::declval<_Tp&>()) == 0);
else
- return noexcept(bool(_Begin{}(std::declval<_Tp>())
- == _End{}(std::declval<_Tp>())));
+ return noexcept(bool(_Begin{}(std::declval<_Tp&>())
+ == _End{}(std::declval<_Tp&>())));
}
public:
template<typename _Tp>
requires __member_empty<_Tp> || __size0_empty<_Tp>
- || __eq_iter_empty<_Tp>
+ || __eq_iter_empty<_Tp>
constexpr bool
- operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
+ operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp&>())
{
if constexpr (__member_empty<_Tp>)
- return bool(std::forward<_Tp>(__e).empty());
+ return bool(__t.empty());
else if constexpr (__size0_empty<_Tp>)
- return _Size{}(std::forward<_Tp>(__e)) == 0;
+ return _Size{}(__t) == 0;
else
- return bool(_Begin{}(std::forward<_Tp>(__e))
- == _End{}(std::forward<_Tp>(__e)));
+ return bool(_Begin{}(__t) == _End{}(__t));
}
};
@@ -510,12 +495,12 @@ namespace ranges
&& is_object_v<remove_pointer_t<_Tp>>;
template<typename _Tp>
- concept __member_data = is_lvalue_reference_v<_Tp>
- && requires(_Tp __t) { { __t.data() } -> __pointer_to_object; };
+ concept __member_data
+ = requires(_Tp& __t) { { __t.data() } -> __pointer_to_object; };
template<typename _Tp>
- concept __begin_data = requires(_Tp&& __t)
- { { _Begin{}(std::forward<_Tp>(__t)) } -> contiguous_iterator; };
+ concept __begin_data = requires(_Tp& __t)
+ { { _Begin{}(__t) } -> contiguous_iterator; };
struct _Data
{
@@ -525,21 +510,21 @@ namespace ranges
_S_noexcept()
{
if constexpr (__member_data<_Tp>)
- return noexcept(__decay_copy(std::declval<_Tp>().data()));
+ return noexcept(__decay_copy(std::declval<_Tp&>().data()));
else
- return noexcept(_Begin{}(std::declval<_Tp>()));
+ return noexcept(_Begin{}(std::declval<_Tp&>()));
}
public:
template<__maybe_borrowed_range _Tp>
requires __member_data<_Tp> || __begin_data<_Tp>
constexpr auto
- operator()(_Tp&& __e) const noexcept(_S_noexcept<_Tp>())
+ operator()(_Tp&& __t) const noexcept(_S_noexcept<_Tp>())
{
if constexpr (__member_data<_Tp>)
- return __e.data();
+ return __t.data();
else
- return std::to_address(_Begin{}(std::forward<_Tp>(__e)));
+ return std::to_address(_Begin{}(__t));
}
};
@@ -548,10 +533,10 @@ namespace ranges
template<typename _Tp>
constexpr auto
operator()(_Tp&& __e) const
- noexcept(noexcept(_Data{}(__cust_access::__as_const((_Tp&&)__e))))
- requires requires { _Data{}(__cust_access::__as_const((_Tp&&)__e)); }
+ noexcept(noexcept(_Data{}(__cust_access::__as_const<_Tp>(__e))))
+ requires requires { _Data{}(__cust_access::__as_const<_Tp>(__e)); }
{
- return _Data{}(__cust_access::__as_const(std::forward<_Tp>(__e)));
+ return _Data{}(__cust_access::__as_const<_Tp>(__e));
}
};
@@ -21,6 +21,10 @@
#include <ranges>
#include <testsuite_hooks.h>
+template<typename T>
+ concept has_cdata
+ = requires (T&& t) { std::ranges::cdata(std::forward<T>(t)); };
+
void
test01()
{
@@ -31,12 +35,18 @@ test01()
int* data() { return &j; }
const R* data() const noexcept { return nullptr; }
};
+ static_assert( has_cdata<R&> );
+ static_assert( has_cdata<const R&> );
R r;
const R& c = r;
VERIFY( std::ranges::cdata(r) == (R*)nullptr );
static_assert( noexcept(std::ranges::cdata(r)) );
VERIFY( std::ranges::cdata(c) == (R*)nullptr );
static_assert( noexcept(std::ranges::cdata(c)) );
+
+ // not lvalues and not borrowed ranges
+ static_assert( !has_cdata<R> );
+ static_assert( !has_cdata<const R> );
}
void
@@ -44,28 +54,36 @@ test02()
{
int a[] = { 0, 1 };
VERIFY( std::ranges::cdata(a) == a + 0 );
+
+ static_assert( has_cdata<int(&)[2]> );
+ static_assert( !has_cdata<int(&&)[2]> );
}
-struct R
+struct R3
{
- long l = 0;
+ static inline int i = 0;
+ static inline long l = 0;
- int* data() const { return nullptr; }
- friend long* begin(R&& r); // this function is not defined
- friend const long* begin(const R& r) { return &r.l; }
- friend const short* begin(const R&&); // not defined
+ int* data() &; // this function is not defined
+ friend long* begin(R3&& r); // not defined
+ friend const long* begin(const R3& r) { return &r.l; }
+ friend const short* begin(const R3&&); // not defined
};
-// This is a lie, ranges::begin(R&&) returns a dangling iterator.
-template<> constexpr bool std::ranges::enable_borrowed_range<R> = true;
+template<> constexpr bool std::ranges::enable_borrowed_range<R3> = true;
void
test03()
{
- R r;
- const R& c = r;
+ static_assert( has_cdata<R3&> );
+ static_assert( has_cdata<R3> ); // borrowed range
+ static_assert( has_cdata<const R3&> );
+ static_assert( has_cdata<const R3> ); // borrowed range
+
+ R3 r;
+ const R3& c = r;
VERIFY( std::ranges::cdata(r) == std::ranges::data(c) );
- VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::begin(c) );
+ VERIFY( std::ranges::cdata(std::move(r)) == std::ranges::data(c) );
VERIFY( std::ranges::cdata(std::move(c)) == std::ranges::begin(c) );
}
@@ -22,6 +22,10 @@
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
+template<typename T>
+ concept has_data
+ = requires (T&& t) { std::ranges::data(std::forward<T>(t)); };
+
void
test01()
{
@@ -32,12 +36,18 @@ test01()
int* data() { return &j; }
const R* data() const noexcept { return nullptr; }
};
+ static_assert( has_data<R&> );
+ static_assert( has_data<const R&> );
R r;
const R& c = r;
VERIFY( std::ranges::data(r) == &r.j );
static_assert( !noexcept(std::ranges::data(r)) );
VERIFY( std::ranges::data(c) == (R*)nullptr );
static_assert( noexcept(std::ranges::data(c)) );
+
+ // not lvalues and not borrowed ranges
+ static_assert( !has_data<R> );
+ static_assert( !has_data<const R> );
}
@@ -49,31 +59,42 @@ test02()
__gnu_test::test_range<int, __gnu_test::contiguous_iterator_wrapper> r(a);
VERIFY( std::ranges::data(r) == std::to_address(std::ranges::begin(r)) );
+
+ static_assert( has_data<int(&)[2]> );
+ static_assert( has_data<decltype(r)&> );
+ static_assert( !has_data<int(&&)[2]> );
+ static_assert( !has_data<decltype(r)&&> );
}
struct R3
{
- long l = 0;
+ static inline int i;
+ static inline long l;
- int* data() const { return nullptr; }
- friend long* begin(R3& r) { return &r.l; }
- friend const long* begin(const R3& r) { return &r.l + 1; }
+ int* data() & { return &i; }
+ friend long* begin(const R3& r) { return &l; }
+ friend const short* begin(const R3&&); // not defined
};
-// N.B. this is a lie, begin on an R3 rvalue will return a dangling pointer.
template<> constexpr bool std::ranges::enable_borrowed_range<R3> = true;
void
test03()
{
+ static_assert( has_data<R3&> );
+ static_assert( has_data<R3> ); // borrowed range
+ static_assert( has_data<const R3&> );
+ static_assert( has_data<const R3> ); // borrowed range
+
R3 r;
const R3& c = r;
- // r.data() can only be used on an lvalue, but ranges::begin(R3&&) is OK
- // because R3 satisfies ranges::borrowed_range.
- VERIFY( std::ranges::data(std::move(r)) == std::to_address(std::ranges::begin(std::move(r))) );
- VERIFY( std::ranges::data(std::move(c)) == std::to_address(std::ranges::begin(std::move(c))) );
+ // PR libstdc++/100824
+ // ranges::data should treat the subexpression as an lvalue
+ VERIFY( std::ranges::data(std::move(r)) == &R3::i );
+ VERIFY( std::ranges::data(std::move(c)) == &R3::l );
}
+
int
main()
{
@@ -35,7 +35,9 @@ test01()
constexpr R r;
static_assert( !std::ranges::empty(r) );
static_assert( same_as<decltype(std::ranges::empty(r)), bool> );
- static_assert( std::ranges::empty(std::move(r)) );
+ // PR libstdc++/100824
+ // ranges::empty should treat the subexpression as an lvalue
+ static_assert( !std::ranges::empty(std::move(r)) );
static_assert( same_as<decltype(std::ranges::empty(std::move(r))), bool> );
}
@@ -68,9 +70,29 @@ test02()
VERIFY( !std::ranges::empty(so) );
}
+void
+test03()
+{
+ // PR libstdc++/100824
+ // ranges::empty should treat the subexpression as an lvalue
+
+ struct R
+ {
+ constexpr bool empty() & { return true; }
+ };
+ static_assert( std::ranges::empty(R{}) );
+
+ struct R2
+ {
+ constexpr unsigned size() & { return 0; }
+ };
+ static_assert( std::ranges::empty(R2{}) );
+}
+
int
main()
{
test01();
test02();
+ test03();
}
@@ -76,12 +76,14 @@ test03()
const R3& c = r;
VERIFY( std::ranges::size(r) == 1 );
static_assert( noexcept(std::ranges::size(r)) );
- VERIFY( std::ranges::size(std::move(r)) == 3U );
- static_assert( !noexcept(std::ranges::size(std::move(r))) );
+ // PR libstdc++/100824
+ // ranges::size should treat the subexpression as an lvalue
+ VERIFY( std::ranges::size(std::move(r)) == 1 );
+ static_assert( noexcept(std::ranges::size(std::move(r))) );
VERIFY( std::ranges::size(c) == 2L );
static_assert( !noexcept(std::ranges::size(c)) );
- VERIFY( std::ranges::size(std::move(c)) == 4UL );
- static_assert( noexcept(std::ranges::size(std::move(c))) );
+ VERIFY( std::ranges::size(std::move(c)) == 2L );
+ static_assert( !noexcept(std::ranges::size(std::move(c))) );
}
void
@@ -109,6 +111,15 @@ test05()
VERIFY( std::ranges::size(r) == 1 );
}
+void
+test06()
+{
+ // PR libstdc++/100824
+ // ranges::size should treat the subexpression as an lvalue
+ struct R { constexpr int size() & { return 42; } };
+ static_assert( std::ranges::size(R{}) == 42 );
+}
+
int
main()
{
@@ -117,4 +128,5 @@ main()
test03();
test04();
test05();
+ test06();
}