Implement LWG-3798 Rvalue reference and `iterator_category` (#3359)

Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
A. Jiang 2023-01-28 10:58:32 +08:00 коммит произвёл GitHub
Родитель 1a28409fbb
Коммит 7ab764dc39
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 203 добавлений и 37 удалений

Просмотреть файл

@ -310,7 +310,7 @@ struct _Iter_traits_category2<false> {
// clang-format off // clang-format off
template <class _It> template <class _It>
concept _Cpp17_forward_delta = constructible_from<_It> && is_lvalue_reference_v<iter_reference_t<_It>> concept _Cpp17_forward_delta = constructible_from<_It> && is_reference_v<iter_reference_t<_It>>
&& same_as<remove_cvref_t<iter_reference_t<_It>>, typename indirectly_readable_traits<_It>::value_type> && same_as<remove_cvref_t<iter_reference_t<_It>>, typename indirectly_readable_traits<_It>::value_type>
&& requires(_It __i) { && requires(_It __i) {
{ __i++ } -> convertible_to<const _It&>; { __i++ } -> convertible_to<const _It&>;

Просмотреть файл

@ -1311,7 +1311,7 @@ namespace ranges {
is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened
: _Value(_STD move(_Value_)), _Bound(_STD move(_Bound_)) { : _Value(_STD move(_Value_)), _Bound(_STD move(_Bound_)) {
if constexpr (totally_ordered_with<_Wi, _Bo>) { if constexpr (totally_ordered_with<_Wi, _Bo>) {
_STL_ASSERT(_Value_ <= _Bound_, "Per N4878 [range.iota.view]/8, the first argument must precede the " _STL_ASSERT(_Value_ <= _Bound_, "Per N4928 [range.iota.view]/8, the first argument must precede the "
"second when their types are totally ordered."); "second when their types are totally ordered.");
} }
} }
@ -2018,7 +2018,7 @@ namespace ranges {
_NODISCARD constexpr _Iterator begin() { _NODISCARD constexpr _Iterator begin() {
#if _CONTAINER_DEBUG_LEVEL > 0 #if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY( _STL_VERIFY(
_Pred, "N4861 [range.filter.view]/3 forbids calling begin on a filter_view that holds no predicate"); _Pred, "N4928 [range.filter.view]/3 forbids calling begin on a filter_view that holds no predicate");
#endif // _CONTAINER_DEBUG_LEVEL > 0 #endif // _CONTAINER_DEBUG_LEVEL > 0
if constexpr (forward_range<_Vw>) { if constexpr (forward_range<_Vw>) {
if (this->_Has_cache()) { if (this->_Has_cache()) {
@ -2091,12 +2091,12 @@ namespace ranges {
template <bool _Const> template <bool _Const>
requires forward_range<_Maybe_const<_Const, _Vw>> requires forward_range<_Maybe_const<_Const, _Vw>>
struct _Category_base<_Const> { struct _Category_base<_Const> {
using _Base = _Maybe_const<_Const, _Vw>; using _Base = _Maybe_const<_Const, _Vw>;
using iterator_category = conditional_t< using iterator_category =
is_lvalue_reference_v<invoke_result_t<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>>>, conditional_t<is_reference_v<invoke_result_t<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>>>,
conditional_t<derived_from<_Iter_cat_t<iterator_t<_Base>>, contiguous_iterator_tag>, conditional_t<derived_from<_Iter_cat_t<iterator_t<_Base>>, contiguous_iterator_tag>,
random_access_iterator_tag, _Iter_cat_t<iterator_t<_Base>>>, random_access_iterator_tag, _Iter_cat_t<iterator_t<_Base>>>,
input_iterator_tag>; input_iterator_tag>;
}; };
template <bool _Const> template <bool _Const>
@ -3015,7 +3015,7 @@ namespace ranges {
is_nothrow_move_constructible_v<_Vw>) // strengthened is_nothrow_move_constructible_v<_Vw>) // strengthened
: _Range(_STD move(_Range_)), _Count{_Count_} { : _Range(_STD move(_Range_)), _Count{_Count_} {
#if _CONTAINER_DEBUG_LEVEL > 0 #if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4861 [range.drop.view]/1"); _STL_VERIFY(_Count_ >= 0, "Number of elements to drop must be non-negative (N4928 [range.drop.view]/1");
#endif // _CONTAINER_DEBUG_LEVEL > 0 #endif // _CONTAINER_DEBUG_LEVEL > 0
} }
@ -3216,7 +3216,7 @@ namespace ranges {
_NODISCARD constexpr auto begin() { _NODISCARD constexpr auto begin() {
#if _CONTAINER_DEBUG_LEVEL > 0 #if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY( _STL_VERIFY(
_Pred, "N4885 [range.drop.while.view] forbids calling begin on a drop_while_view with no predicate"); _Pred, "N4928 [range.drop.while.view]/3 forbids calling begin on a drop_while_view with no predicate");
#endif // _CONTAINER_DEBUG_LEVEL > 0 #endif // _CONTAINER_DEBUG_LEVEL > 0
if constexpr (forward_range<_Vw>) { if constexpr (forward_range<_Vw>) {
if (this->_Has_cache()) { if (this->_Has_cache()) {
@ -3716,7 +3716,7 @@ namespace ranges {
using _PatternBase = _Maybe_const<_Const, _Pat>; using _PatternBase = _Maybe_const<_Const, _Pat>;
using iterator_category = conditional_t< using iterator_category = conditional_t<
!is_lvalue_reference_v<common_reference_t<range_reference_t<_Inner>, range_reference_t<_PatternBase>>>, !is_reference_v<common_reference_t<range_reference_t<_Inner>, range_reference_t<_PatternBase>>>,
input_iterator_tag, input_iterator_tag,
conditional_t<common_range<_Inner> && common_range<_PatternBase> conditional_t<common_range<_Inner> && common_range<_PatternBase>
&& derived_from<_Iter_cat_t<iterator_t<_Outer>>, bidirectional_iterator_tag> && derived_from<_Iter_cat_t<iterator_t<_Outer>>, bidirectional_iterator_tag>
@ -6483,7 +6483,7 @@ namespace ranges {
is_nothrow_move_constructible_v<_Vw>) /* strengthened */ is_nothrow_move_constructible_v<_Vw>) /* strengthened */
: _Range(_STD move(_Range_)), _Count{_Count_} { : _Range(_STD move(_Range_)), _Count{_Count_} {
#if _CONTAINER_DEBUG_LEVEL > 0 #if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Count > 0, "The window size must be positive (N4917 [range.slide.view]/1)"); _STL_VERIFY(_Count > 0, "The window size must be positive (N4928 [range.slide.view]/1)");
#endif // _CONTAINER_DEBUG_LEVEL > 0 #endif // _CONTAINER_DEBUG_LEVEL > 0
} }

Просмотреть файл

@ -204,6 +204,56 @@ struct std::indirectly_readable_traits<simple_contiguous_iter<Base>> {
using value_type = double; using value_type = double;
}; };
// Validate iterator_category of iterators whose reference types are rvalue references (LWG-3798).
struct xvalue_forward_iter {
using value_type = double;
using difference_type = long;
value_type&& operator*() const;
xvalue_forward_iter& operator++();
xvalue_forward_iter operator++(int);
bool operator==(xvalue_forward_iter const&) const = default;
};
struct xvalue_bidi_iter {
using value_type = double;
using difference_type = long;
using reference = value_type&&;
value_type&& operator*() const;
xvalue_bidi_iter& operator++();
xvalue_bidi_iter operator++(int);
bool operator==(xvalue_bidi_iter const&) const = default;
xvalue_bidi_iter& operator--();
xvalue_bidi_iter operator--(int);
};
struct xvalue_random_iter {
using value_type = double;
using D = long;
value_type&& operator*() const;
xvalue_random_iter& operator++();
xvalue_random_iter operator++(int);
xvalue_random_iter& operator--();
xvalue_random_iter operator--(int);
bool operator==(xvalue_random_iter const&) const;
std::strong_ordering operator<=>(xvalue_random_iter const&) const;
value_type&& operator[](D) const;
xvalue_random_iter& operator-=(D);
xvalue_random_iter operator-(D) const;
D operator-(xvalue_random_iter const&) const;
xvalue_random_iter& operator+=(D);
xvalue_random_iter operator+(D) const;
friend xvalue_random_iter operator+(D, xvalue_random_iter const&);
};
template <int I> template <int I>
struct proxy_iterator { struct proxy_iterator {
using difference_type = int; using difference_type = int;
@ -592,7 +642,7 @@ inline constexpr std::size_t contig_iterator_archetype_max = 34;
#pragma warning(pop) #pragma warning(pop)
struct iter_concept_example { struct iter_concept_example {
// bidirectional_iterator and Cpp17InputIterator, but not Cpp17ForwardIterator (N4820 [iterator.concepts.general]/2) // bidirectional_iterator and Cpp17InputIterator, but not Cpp17ForwardIterator (N4928 [iterator.concepts.general]/2)
using value_type = int; using value_type = int;
using difference_type = int; using difference_type = int;
@ -866,43 +916,45 @@ namespace iterator_traits_test {
return true; return true;
} }
// N4820 [iterator.traits]/3.2: "Otherwise, if I satisfies the exposition-only concept cpp17-input-iterator..." // N4928 [iterator.traits]/3.2: "Otherwise, if I satisfies the exposition-only concept cpp17-input-iterator..."
// N4820 [iterator.traits]: // N4928 [iterator.traits]:
// * 3.2.1: "... Otherwise, pointer names void." // * 3.2.1: "... Otherwise, pointer names void."
// * 3.2.2: "... Otherwise, reference names iter_reference_t<I>." // * 3.2.2: "... Otherwise, reference names iter_reference_t<I>."
// * 3.2.3.4 "... Otherwise, iterator_category names... input_iterator_tag." // * 3.2.3.4 "... Otherwise, iterator_category names... input_iterator_tag."
STATIC_ASSERT(check<simple_input_iter, no_such_type, input_iterator_tag, double, long, void, double>()); STATIC_ASSERT(check<simple_input_iter, no_such_type, input_iterator_tag, double, long, void, double>());
// N4820 [iterator.traits]: // N4928 [iterator.traits]:
// * 3.2.1: "... Otherwise, pointer names void." // * 3.2.1: "... Otherwise, pointer names void."
// * 3.2.2: "... Otherwise, reference names iter_reference_t<I>." // * 3.2.2: "... Otherwise, reference names iter_reference_t<I>."
// * 3.2.3.3 "... Otherwise, iterator_category names... forward_iterator_tag if I satisfies cpp17-forward-iterator." // * 3.2.3.3 "... Otherwise, iterator_category names... forward_iterator_tag if I satisfies cpp17-forward-iterator."
STATIC_ASSERT( STATIC_ASSERT(
check<simple_forward_iter<>, no_such_type, forward_iterator_tag, double, long, void, double const&>()); check<simple_forward_iter<>, no_such_type, forward_iterator_tag, double, long, void, double const&>());
// N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval<I&>().operator->()) is well-formed, then STATIC_ASSERT(check<xvalue_forward_iter, no_such_type, forward_iterator_tag, double, long, void, double&&>());
// N4928 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval<I&>().operator->()) is well-formed, then
// pointer names that type." // pointer names that type."
STATIC_ASSERT(check<simple_forward_iter<arrow_base<double const*>>, no_such_type, forward_iterator_tag, double, STATIC_ASSERT(check<simple_forward_iter<arrow_base<double const*>>, no_such_type, forward_iterator_tag, double,
long, double const*, double const&>()); long, double const*, double const&>());
// N4820 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, // N4928 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type,
// iterator_category names that type." // iterator_category names that type."
STATIC_ASSERT(check<simple_forward_iter<with_iter_cat<output_iterator_tag>>, no_such_type, output_iterator_tag, STATIC_ASSERT(check<simple_forward_iter<with_iter_cat<output_iterator_tag>>, no_such_type, output_iterator_tag,
double, long, void, double const&>()); double, long, void, double const&>());
STATIC_ASSERT(check<simple_forward_iter<with_iter_cat<input_iterator_tag>>, no_such_type, input_iterator_tag, STATIC_ASSERT(check<simple_forward_iter<with_iter_cat<input_iterator_tag>>, no_such_type, input_iterator_tag,
double, long, void, double const&>()); double, long, void, double const&>());
// N4820 [iterator.traits]: // N4928 [iterator.traits]:
// * 3.2.1: "... Otherwise, pointer names void." // * 3.2.1: "... Otherwise, pointer names void."
// * 3.2.2: "If the qualified-id I::reference is valid and denotes a type, reference names that type." // * 3.2.2: "If the qualified-id I::reference is valid and denotes a type, reference names that type."
// * 3.2.3.2 "... Otherwise, iterator_category names... bidirectional_iterator_tag if I satisfies // * 3.2.3.2 "... Otherwise, iterator_category names... bidirectional_iterator_tag if I satisfies
// cpp17-bidirectional-iterator." // cpp17-bidirectional-iterator."
STATIC_ASSERT( STATIC_ASSERT(
check<simple_bidi_iter<>, no_such_type, bidirectional_iterator_tag, double, long, void, double const&>()); check<simple_bidi_iter<>, no_such_type, bidirectional_iterator_tag, double, long, void, double const&>());
// N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval<I&>().operator->()) is well-formed, then STATIC_ASSERT(check<xvalue_bidi_iter, no_such_type, bidirectional_iterator_tag, double, long, void, double&&>());
// N4928 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval<I&>().operator->()) is well-formed, then
// pointer names that type." // pointer names that type."
STATIC_ASSERT(check<simple_bidi_iter<arrow_base<double const*>>, no_such_type, bidirectional_iterator_tag, double, STATIC_ASSERT(check<simple_bidi_iter<arrow_base<double const*>>, no_such_type, bidirectional_iterator_tag, double,
long, double const*, double const&>()); long, double const*, double const&>());
// N4820 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, // N4928 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type,
// iterator_category names that type." // iterator_category names that type."
STATIC_ASSERT(check<simple_bidi_iter<with_iter_cat<output_iterator_tag>>, no_such_type, output_iterator_tag, double, STATIC_ASSERT(check<simple_bidi_iter<with_iter_cat<output_iterator_tag>>, no_such_type, output_iterator_tag, double,
long, void, double const&>()); long, void, double const&>());
@ -911,22 +963,23 @@ namespace iterator_traits_test {
STATIC_ASSERT(check<simple_bidi_iter<with_iter_cat<forward_iterator_tag>>, no_such_type, forward_iterator_tag, STATIC_ASSERT(check<simple_bidi_iter<with_iter_cat<forward_iterator_tag>>, no_such_type, forward_iterator_tag,
double, long, void, double const&>()); double, long, void, double const&>());
// N4820 [iterator.traits]: // N4928 [iterator.traits]:
// * 3.2.1: "... Otherwise, pointer names void." // * 3.2.1: "... Otherwise, pointer names void."
// * 3.2.2: "... Otherwise, reference names iter_reference_t<I>." // * 3.2.2: "... Otherwise, reference names iter_reference_t<I>."
// * 3.2.3.1 "... Otherwise, iterator_category names... random_access_iterator_tag if I satisfies // * 3.2.3.1 "... Otherwise, iterator_category names... random_access_iterator_tag if I satisfies
// cpp17-random-access-iterator." // cpp17-random-access-iterator."
STATIC_ASSERT( STATIC_ASSERT(
check<simple_random_iter<>, no_such_type, random_access_iterator_tag, double, long, void, double const&>()); check<simple_random_iter<>, no_such_type, random_access_iterator_tag, double, long, void, double const&>());
STATIC_ASSERT(check<xvalue_random_iter, no_such_type, random_access_iterator_tag, double, long, void, double&&>());
STATIC_ASSERT( STATIC_ASSERT(
check<simple_contiguous_iter<>, no_such_type, random_access_iterator_tag, double, long, void, double const&>()); check<simple_contiguous_iter<>, no_such_type, random_access_iterator_tag, double, long, void, double const&>());
// N4820 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval<I&>().operator->()) is well-formed, then // N4928 [iterator.traits]/3.2.1: "... Otherwise, if decltype(declval<I&>().operator->()) is well-formed, then
// pointer names that type." // pointer names that type."
STATIC_ASSERT(check<simple_random_iter<arrow_base<double const*>>, no_such_type, random_access_iterator_tag, double, STATIC_ASSERT(check<simple_random_iter<arrow_base<double const*>>, no_such_type, random_access_iterator_tag, double,
long, double const*, double const&>()); long, double const*, double const&>());
STATIC_ASSERT(check<simple_contiguous_iter<arrow_base<double const*>>, no_such_type, random_access_iterator_tag, STATIC_ASSERT(check<simple_contiguous_iter<arrow_base<double const*>>, no_such_type, random_access_iterator_tag,
double, long, double const*, double const&>()); double, long, double const*, double const&>());
// N4820 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type, // N4928 [iterator.traits]/3.2.3: "If the qualified-id I::iterator_category is valid and denotes a type,
// iterator_category names that type." // iterator_category names that type."
STATIC_ASSERT(check<simple_random_iter<with_iter_cat<output_iterator_tag>>, no_such_type, output_iterator_tag, STATIC_ASSERT(check<simple_random_iter<with_iter_cat<output_iterator_tag>>, no_such_type, output_iterator_tag,
double, long, void, double const&>()); double, long, void, double const&>());
@ -945,7 +998,7 @@ namespace iterator_traits_test {
STATIC_ASSERT(check<simple_contiguous_iter<with_iter_cat<contiguous_iterator_tag>>, no_such_type, STATIC_ASSERT(check<simple_contiguous_iter<with_iter_cat<contiguous_iterator_tag>>, no_such_type,
contiguous_iterator_tag, double, long, void, double const&>()); contiguous_iterator_tag, double, long, void, double const&>());
// N4820 [iterator.traits]/3.3: "Otherwise, if I satisfies the exposition-only concept cpp17-iterator..." // N4928 [iterator.traits]/3.3: "Otherwise, if I satisfies the exposition-only concept cpp17-iterator..."
template <class Base = empty_type> template <class Base = empty_type>
struct simple_output_iter : Base { struct simple_output_iter : Base {
simple_output_iter const& operator*() const; simple_output_iter const& operator*() const;
@ -959,13 +1012,13 @@ namespace iterator_traits_test {
// "... otherwise, it names void." // "... otherwise, it names void."
STATIC_ASSERT(check<simple_output_iter<>, no_such_type, output_iterator_tag, void, void, void, void>()); STATIC_ASSERT(check<simple_output_iter<>, no_such_type, output_iterator_tag, void, void, void, void>());
// N4820 [iterator.traits]/3.4: "Otherwise, iterator_traits has no members by any of the above names." // N4928 [iterator.traits]/3.4: "Otherwise, iterator_traits<I> has no members by any of the above names."
STATIC_ASSERT(has_empty_traits<int>); STATIC_ASSERT(has_empty_traits<int>);
STATIC_ASSERT(has_empty_traits<void>); STATIC_ASSERT(has_empty_traits<void>);
STATIC_ASSERT(has_empty_traits<int(int)>); STATIC_ASSERT(has_empty_traits<int(int)>);
STATIC_ASSERT(has_empty_traits<int(int) const>); STATIC_ASSERT(has_empty_traits<int(int) const>);
// N4820 [iterator.traits]/5: "iterator_traits is specialized for pointers..." // N4928 [iterator.traits]/5: "iterator_traits is specialized for pointers..."
STATIC_ASSERT(check<int*, contiguous_iterator_tag, random_access_iterator_tag, int, std::ptrdiff_t, int*, int&>()); STATIC_ASSERT(check<int*, contiguous_iterator_tag, random_access_iterator_tag, int, std::ptrdiff_t, int*, int&>());
STATIC_ASSERT(check<int const*, contiguous_iterator_tag, random_access_iterator_tag, int, std::ptrdiff_t, STATIC_ASSERT(check<int const*, contiguous_iterator_tag, random_access_iterator_tag, int, std::ptrdiff_t,
int const*, int const&>()); int const*, int const&>());
@ -986,7 +1039,8 @@ namespace iterator_cust_move_test {
template <class T> template <class T>
concept can_iter_rvalue_ref = requires { typename iter_rvalue_reference_t<T>; }; concept can_iter_rvalue_ref = requires { typename iter_rvalue_reference_t<T>; };
// N4820 [iterator.cust.move]/1.1 "iter_move(E), if that expression is valid, with overload resolution..." // N4928 [iterator.cust.move]/1.1 "iter_move(E), if [...] iter_move(E) is a well-formed expression when [...]
// performing argument-dependent lookup only."
struct friend_hook { struct friend_hook {
friend constexpr double iter_move(friend_hook) noexcept { friend constexpr double iter_move(friend_hook) noexcept {
return 3.14; return 3.14;
@ -1015,7 +1069,7 @@ namespace iterator_cust_move_test {
STATIC_ASSERT(static_cast<int>(ranges::iter_move(E1::x)) == 0); STATIC_ASSERT(static_cast<int>(ranges::iter_move(E1::x)) == 0);
STATIC_ASSERT(noexcept(ranges::iter_move(E1::x))); STATIC_ASSERT(noexcept(ranges::iter_move(E1::x)));
// N4820 [iterator.cust.move]/1.2.1 "if *E is an lvalue, std::move(*E)" // N4928 [iterator.cust.move]/1.2.1 "if *E is an lvalue, std::move(*E)"
static constexpr int some_ints[] = {0, 1, 2, 3}; static constexpr int some_ints[] = {0, 1, 2, 3};
STATIC_ASSERT(same_as<iter_rvalue_reference_t<int*>, int&&>); STATIC_ASSERT(same_as<iter_rvalue_reference_t<int*>, int&&>);
STATIC_ASSERT(ranges::iter_move(&some_ints[1]) == 1); STATIC_ASSERT(ranges::iter_move(&some_ints[1]) == 1);
@ -1062,7 +1116,7 @@ namespace iterator_cust_move_test {
STATIC_ASSERT(same_as<iter_rvalue_reference_t<with_bogus_typedefs>, int const&&>); // oblivious to nested types STATIC_ASSERT(same_as<iter_rvalue_reference_t<with_bogus_typedefs>, int const&&>); // oblivious to nested types
STATIC_ASSERT(ranges::iter_move(with_bogus_typedefs{}) == 1); STATIC_ASSERT(ranges::iter_move(with_bogus_typedefs{}) == 1);
// N4820 [iterator.cust.move]/1.2.2 "otherwise, *E." // N4928 [iterator.cust.move]/1.2.2 "otherwise, *E."
struct ref_is_prvalue { struct ref_is_prvalue {
int operator*() const { int operator*() const {
return 42; return 42;
@ -1076,7 +1130,7 @@ namespace iterator_cust_move_test {
STATIC_ASSERT(same_as<iter_rvalue_reference_t<ref_is_xvalue>, int&&>); STATIC_ASSERT(same_as<iter_rvalue_reference_t<ref_is_xvalue>, int&&>);
STATIC_ASSERT(!noexcept(ranges::iter_move(ref_is_xvalue{}))); STATIC_ASSERT(!noexcept(ranges::iter_move(ref_is_xvalue{})));
// N4820 [iterator.cust.move]/1.3 "Otherwise, ranges::iter_move(E) is ill-formed." // N4928 [iterator.cust.move]/1.3 "Otherwise, ranges::iter_move(E) is ill-formed."
STATIC_ASSERT(!can_iter_move<int>); STATIC_ASSERT(!can_iter_move<int>);
STATIC_ASSERT(!can_iter_move<void>); STATIC_ASSERT(!can_iter_move<void>);
STATIC_ASSERT(!can_iter_move<int(int) const>); STATIC_ASSERT(!can_iter_move<int(int) const>);
@ -1093,7 +1147,8 @@ namespace iterator_cust_swap_test {
template <class T, class U> template <class T, class U>
concept can_iter_swap = requires(T&& t, U&& u) { ranges::iter_swap(std::forward<T>(t), std::forward<U>(u)); }; concept can_iter_swap = requires(T&& t, U&& u) { ranges::iter_swap(std::forward<T>(t), std::forward<U>(u)); };
// N4820 [iterator.cust.swap]/4.1: "(void)iter_swap(E1, E2), if that expression is valid, with..." // N4928 [iterator.cust.swap]/4.1: "(void)iter_swap(E1, E2), if [...] iter_swap(E1, E2) is a
// well-formed expression with overload resolution performed in a context [...]"
namespace adl_barrier { namespace adl_barrier {
template <class T, class U> template <class T, class U>
void iter_swap(T, U) = delete; void iter_swap(T, U) = delete;
@ -1127,8 +1182,8 @@ namespace iterator_cust_swap_test {
STATIC_ASSERT(bullet1<E1>); STATIC_ASSERT(bullet1<E1>);
STATIC_ASSERT((ranges::iter_swap(E1::x, E1::x), true)); STATIC_ASSERT((ranges::iter_swap(E1::x, E1::x), true));
// N4849 [iterator.cust.swap]/4.2: "Otherwise if the types of E1 and E2 each model indirectly_readable, and if the // N4928 [iterator.cust.swap]/4.2: "Otherwise, if the types of E1 and E2 each model indirectly_readable,
// reference types of E1 and E2 model swappable_with, then ranges::swap(*E1, *E2)." // and if the reference types of E1 and E2 model swappable_with, then ranges::swap(*E1, *E2)."
// clang-format off // clang-format off
template <class T, class U = T> template <class T, class U = T>
concept bullet2 = !bullet1<T, U> && indirectly_readable<remove_reference_t<T>> concept bullet2 = !bullet1<T, U> && indirectly_readable<remove_reference_t<T>>
@ -1178,7 +1233,7 @@ namespace iterator_cust_swap_test {
STATIC_ASSERT((ranges::iter_swap(swap_proxy_readable<0>{}, swap_proxy_readable<1>{}), true)); STATIC_ASSERT((ranges::iter_swap(swap_proxy_readable<0>{}, swap_proxy_readable<1>{}), true));
STATIC_ASSERT(noexcept(ranges::iter_swap(swap_proxy_readable<0>{}, swap_proxy_readable<1>{}))); STATIC_ASSERT(noexcept(ranges::iter_swap(swap_proxy_readable<0>{}, swap_proxy_readable<1>{})));
// N4820 [iterator.cust.swap]/4.3: "Otherwise, if the types T1 and T2 of E1 and E2 model // N4928 [iterator.cust.swap]/4.3: "Otherwise, if the types T1 and T2 of E1 and E2 model
// indirectly_movable_storable<T1, T2> and indirectly_movable_storable<T2, T1>..." // indirectly_movable_storable<T1, T2> and indirectly_movable_storable<T2, T1>..."
// clang-format off // clang-format off
template <class T, class U = T> template <class T, class U = T>
@ -1204,7 +1259,7 @@ namespace iterator_cust_swap_test {
STATIC_ASSERT(same_as<decltype(ranges::iter_swap(unswap_proxy_readable<0>{}, unswap_proxy_readable<1>{})), void>); STATIC_ASSERT(same_as<decltype(ranges::iter_swap(unswap_proxy_readable<0>{}, unswap_proxy_readable<1>{})), void>);
STATIC_ASSERT(noexcept(ranges::iter_swap(unswap_proxy_readable<0>{}, unswap_proxy_readable<1>{}))); STATIC_ASSERT(noexcept(ranges::iter_swap(unswap_proxy_readable<0>{}, unswap_proxy_readable<1>{})));
// N4820 [iterator.cust.swap]/4.4: "Otherwise, ranges::iter_swap(E1, E2) is ill-formed." // N4928 [iterator.cust.swap]/4.4: "Otherwise, ranges::iter_swap(E1, E2) is ill-formed."
template <class T, class U> template <class T, class U>
concept bullet4 = (!can_iter_swap<T, U>); concept bullet4 = (!can_iter_swap<T, U>);
@ -3092,6 +3147,10 @@ namespace reverse_iterator_test {
STATIC_ASSERT(same_as<reverse_iterator<simple_random_iter<>>::iterator_category, random_access_iterator_tag>); STATIC_ASSERT(same_as<reverse_iterator<simple_random_iter<>>::iterator_category, random_access_iterator_tag>);
STATIC_ASSERT(same_as<reverse_iterator<simple_bidi_iter<>>::iterator_concept, bidirectional_iterator_tag>); STATIC_ASSERT(same_as<reverse_iterator<simple_bidi_iter<>>::iterator_concept, bidirectional_iterator_tag>);
STATIC_ASSERT(same_as<reverse_iterator<simple_bidi_iter<>>::iterator_category, bidirectional_iterator_tag>); STATIC_ASSERT(same_as<reverse_iterator<simple_bidi_iter<>>::iterator_category, bidirectional_iterator_tag>);
STATIC_ASSERT(same_as<reverse_iterator<xvalue_random_iter>::iterator_concept, random_access_iterator_tag>);
STATIC_ASSERT(same_as<reverse_iterator<xvalue_random_iter>::iterator_category, random_access_iterator_tag>);
STATIC_ASSERT(same_as<reverse_iterator<xvalue_bidi_iter>::iterator_concept, bidirectional_iterator_tag>);
STATIC_ASSERT(same_as<reverse_iterator<xvalue_bidi_iter>::iterator_category, bidirectional_iterator_tag>);
// Validate operator-> for a pointer, and for non-pointers with and without operator->() // Validate operator-> for a pointer, and for non-pointers with and without operator->()
// clang-format off // clang-format off
@ -3285,6 +3344,12 @@ namespace move_iterator_test {
STATIC_ASSERT(same_as<move_iterator<simple_input_iter>::iterator_concept, input_iterator_tag>); STATIC_ASSERT(same_as<move_iterator<simple_input_iter>::iterator_concept, input_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<simple_input_iter>::iterator_category, input_iterator_tag>); STATIC_ASSERT(same_as<move_iterator<simple_input_iter>::iterator_category, input_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<input_iter<true>>::iterator_concept, input_iterator_tag>); STATIC_ASSERT(same_as<move_iterator<input_iter<true>>::iterator_concept, input_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<xvalue_random_iter>::iterator_concept, random_access_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<xvalue_random_iter>::iterator_category, random_access_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<xvalue_bidi_iter>::iterator_concept, bidirectional_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<xvalue_bidi_iter>::iterator_category, bidirectional_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<xvalue_forward_iter>::iterator_concept, forward_iterator_tag>);
STATIC_ASSERT(same_as<move_iterator<xvalue_forward_iter>::iterator_category, forward_iterator_tag>);
STATIC_ASSERT(!has_member_iter_category<move_iterator<input_iter<true>>>); STATIC_ASSERT(!has_member_iter_category<move_iterator<input_iter<true>>>);
STATIC_ASSERT(same_as<move_iterator<input_iter<false>>::iterator_concept, input_iterator_tag>); STATIC_ASSERT(same_as<move_iterator<input_iter<false>>::iterator_concept, input_iterator_tag>);
STATIC_ASSERT(!has_member_iter_category<move_iterator<input_iter<false>>>); STATIC_ASSERT(!has_member_iter_category<move_iterator<input_iter<false>>>);
@ -3448,6 +3513,12 @@ namespace counted_iterator_test {
STATIC_ASSERT( STATIC_ASSERT(
same_as<iterator_traits<counted_iterator<simple_forward_iter<>>>::iterator_category, forward_iterator_tag>); same_as<iterator_traits<counted_iterator<simple_forward_iter<>>>::iterator_category, forward_iterator_tag>);
STATIC_ASSERT(same_as<iterator_traits<counted_iterator<simple_input_iter>>::iterator_category, input_iterator_tag>); STATIC_ASSERT(same_as<iterator_traits<counted_iterator<simple_input_iter>>::iterator_category, input_iterator_tag>);
STATIC_ASSERT(
same_as<iterator_traits<counted_iterator<xvalue_random_iter>>::iterator_category, random_access_iterator_tag>);
STATIC_ASSERT(
same_as<iterator_traits<counted_iterator<xvalue_bidi_iter>>::iterator_category, bidirectional_iterator_tag>);
STATIC_ASSERT(
same_as<iterator_traits<counted_iterator<xvalue_forward_iter>>::iterator_category, forward_iterator_tag>);
// Validate postincrement // Validate postincrement
STATIC_ASSERT(same_as<decltype(std::declval<counted_iterator<simple_input_iter>&>()++), simple_input_iter>); STATIC_ASSERT(same_as<decltype(std::declval<counted_iterator<simple_input_iter>&>()++), simple_input_iter>);

Просмотреть файл

@ -363,6 +363,36 @@ constexpr void test_difference_on_const_functor(Rng&& rng) {
} }
} }
// Test xvalue ranges (LWG-3798)
struct move_fn {
constexpr auto&& operator()(auto&& x) const noexcept {
return move(x);
}
};
template <ranges::input_range Rng>
constexpr void test_xvalue_ranges(Rng&& rng) {
using ranges::transform_view, ranges::forward_range, ranges::iterator_t, ranges::range_reference_t;
using V = views::all_t<Rng>;
using TV = transform_view<V, move_fn>;
auto r = forward<Rng>(rng) | views::transform(move_fn{});
STATIC_ASSERT(is_same_v<decltype(r), TV>);
STATIC_ASSERT(is_rvalue_reference_v<range_reference_t<TV>>);
if constexpr (forward_range<V>) {
using It = iterator_t<V>;
using TVIt = iterator_t<TV>;
using VItCat = typename iterator_traits<It>::iterator_category;
using TVItCat = typename iterator_traits<TVIt>::iterator_category;
STATIC_ASSERT(
is_same_v<TVItCat, VItCat>
|| (is_same_v<TVItCat, random_access_iterator_tag> && is_same_v<VItCat, contiguous_iterator_tag>) );
}
}
static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7};
static constexpr int transformed_ints[] = {8, 9, 10, 11, 12, 13, 14, 15}; static constexpr int transformed_ints[] = {8, 9, 10, 11, 12, 13, 14, 15};
@ -374,6 +404,9 @@ struct instantiator {
R r2{some_ints}; R r2{some_ints};
test_difference_on_const_functor(r2); test_difference_on_const_functor(r2);
R r3{some_ints};
test_xvalue_ranges(r3);
} }
}; };

Просмотреть файл

@ -55,6 +55,55 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected)
&& common_range<Inner> && bidirectional_range<DV> && common_range<DV>) ); && common_range<Inner> && bidirectional_range<DV> && common_range<DV>) );
STATIC_ASSERT(!ranges::random_access_range<R>); STATIC_ASSERT(!ranges::random_access_range<R>);
// Validate iterator_category
if constexpr (forward_range<R>) {
using OuterIter = iterator_t<Outer>;
using InnerIter = iterator_t<range_reference_t<Outer>>;
using PatternIter = iterator_t<DV>;
using OuterCat = typename iterator_traits<OuterIter>::iterator_category;
using InnerCat = typename iterator_traits<InnerIter>::iterator_category;
using PatternCat = typename iterator_traits<PatternIter>::iterator_category;
if constexpr (!is_reference_v<common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>>) {
STATIC_ASSERT(same_as<typename iterator_t<R>::iterator_category, input_iterator_tag>);
} else if constexpr (derived_from<OuterCat, bidirectional_iterator_tag>
&& derived_from<InnerCat, bidirectional_iterator_tag>
&& derived_from<PatternCat, bidirectional_iterator_tag>
&& common_range<range_reference_t<Outer>> && common_range<DV>) {
STATIC_ASSERT(same_as<typename iterator_t<R>::iterator_category, bidirectional_iterator_tag>);
} else if constexpr (derived_from<OuterCat, forward_iterator_tag>
&& derived_from<InnerCat, forward_iterator_tag>
&& derived_from<PatternCat, forward_iterator_tag>) {
STATIC_ASSERT(same_as<typename iterator_t<R>::iterator_category, forward_iterator_tag>);
} else {
STATIC_ASSERT(same_as<typename iterator_t<R>::iterator_category, input_iterator_tag>);
}
}
if constexpr (forward_range<const R>) {
using OuterIter = iterator_t<const Outer>;
using InnerIter = iterator_t<range_reference_t<const Outer>>;
using PatternIter = iterator_t<const DV>;
using OuterCat = typename iterator_traits<OuterIter>::iterator_category;
using InnerCat = typename iterator_traits<InnerIter>::iterator_category;
using PatternCat = typename iterator_traits<PatternIter>::iterator_category;
if constexpr (!is_reference_v<common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>>) {
STATIC_ASSERT(same_as<typename iterator_t<const R>::iterator_category, input_iterator_tag>);
} else if constexpr (derived_from<OuterCat, bidirectional_iterator_tag>
&& derived_from<InnerCat, bidirectional_iterator_tag>
&& derived_from<PatternCat, bidirectional_iterator_tag>
&& common_range<range_reference_t<const Outer>> && common_range<const DV>) {
STATIC_ASSERT(same_as<typename iterator_t<const R>::iterator_category, bidirectional_iterator_tag>);
} else if constexpr (derived_from<OuterCat, forward_iterator_tag>
&& derived_from<InnerCat, forward_iterator_tag>
&& derived_from<PatternCat, forward_iterator_tag>) {
STATIC_ASSERT(same_as<typename iterator_t<const R>::iterator_category, forward_iterator_tag>);
} else {
STATIC_ASSERT(same_as<typename iterator_t<const R>::iterator_category, input_iterator_tag>);
}
}
// Validate range adaptor object and range adaptor closure // Validate range adaptor object and range adaptor closure
constexpr bool is_view = ranges::view<remove_cvref_t<Outer>>; constexpr bool is_view = ranges::view<remove_cvref_t<Outer>>;
const auto closure = views::join_with(delimiter); const auto closure = views::join_with(delimiter);
@ -314,6 +363,19 @@ struct instantiator {
Outer empty{span<Inner, 0>{}}; Outer empty{span<Inner, 0>{}};
test_one(empty, "*#"sv, views::empty<char>); test_one(empty, "*#"sv, views::empty<char>);
} }
#ifdef __clang__ // TRANSITION, LLVM-60293
if constexpr (ranges::forward_range<Outer> || ranges::common_range<Outer>)
#endif // __clang__
{ // Range-of-rvalue delimiter
Inner inner_ranges[] = {Inner{span{input[0]}}, Inner{span{input[1]}}, Inner{span{input[2]}},
Inner{span{input[3]}}, Inner{span{input[4]}}, Inner{span{input[5]}}, Inner{span{input[6]}},
Inner{span{input[7]}}};
Outer r{inner_ranges};
test_one(r | views::as_rvalue, "*#"sv | views::as_rvalue, expected_range);
Outer empty{span<Inner, 0>{}};
test_one(empty | views::as_rvalue, "*#"sv | views::as_rvalue, views::empty<char>);
}
} }
}; };