зеркало из https://github.com/microsoft/STL.git
Implement LWG-3798 Rvalue reference and `iterator_category` (#3359)
Co-authored-by: Stephan T. Lavavej <stl@nuwen.net>
This commit is contained in:
Родитель
1a28409fbb
Коммит
7ab764dc39
|
@ -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>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче