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
{
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>)
-> 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;
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>>)
{
}
};
- 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);
}
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));
}
};
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));
}
};
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();
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();
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));
}
};
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;
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{}(__t) } -> sized_sentinel_for<decltype(_Begin{}(__t))>;
- { _End{}(std::forward<_Tp>(__t)) }
- -> sized_sentinel_for<decltype(_Begin{}(std::forward<_Tp>(__t)))>;
+ __detail::__to_unsigned_like(_End{}(__t) - _Begin{}(__t));
};
struct _Size
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:
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
};
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
_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));
}
};
&& 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
{
_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));
}
};
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));
}
};
#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()
{
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> );
}
__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()
{