diff --git a/stl/inc/xutility b/stl/inc/xutility index 6176fa1c5cc..227848d1006 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2842,15 +2842,19 @@ namespace ranges { public: using _Not_quite_object::_Not_quite_object; + // clang-format off template _Se> - _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const - noexcept(_Nothrow_distance<_It, _Se>) /* strengthened */ { - if constexpr (sized_sentinel_for<_Se, _It>) { - return _Last - _First; - } else { - _Adl_verify_range(_First, _Last); - return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); - } + requires (!sized_sentinel_for<_Se, _It>) + _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const { + // clang-format on + _Adl_verify_range(_First, _Last); + return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); + } + + template _Se> + _NODISCARD constexpr iter_difference_t<_It> operator()(const _It& _First, const _Se& _Last) const + noexcept(noexcept(_Last - _First)) /* strengthened */ { + return _Last - _First; } template @@ -2866,8 +2870,7 @@ namespace ranges { private: template _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept( - _Nothrow_distance<_It, _Se>) { - _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); + noexcept(++_First != _Last)) { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); iter_difference_t<_It> _Count = 0; @@ -2878,15 +2881,10 @@ namespace ranges { return _Count; } - template - static constexpr bool _Nothrow_distance = noexcept( - noexcept(++_STD declval<_Unwrapped_t<_It>&>() != _STD declval&>())); - template _Se> - static constexpr bool _Nothrow_distance<_It, _Se> = noexcept( - noexcept(_STD declval<_Se&>() - _STD declval<_It&>())); - template - static constexpr bool _Nothrow_size = _Nothrow_distance, sentinel_t<_Rng>>; + static constexpr bool _Nothrow_size = noexcept( + _Distance_unchecked(_Ubegin(_STD declval<_Rng&>()), _Uend(_STD declval<_Rng&>()))); + template static constexpr bool _Nothrow_size<_Rng> = noexcept(_RANGES size(_STD declval<_Rng&>())); }; diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 425dfe6dd18..ffb85b7fae6 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -1877,10 +1877,11 @@ namespace iter_ops { enum class sized { no, yes }; enum class assign { no, yes }; + enum class nothrow { no, yes }; static constexpr int sentinel_position = 42; - template + template struct trace_iterator { using value_type = int; using difference_type = int; @@ -1891,13 +1892,15 @@ namespace iter_ops { static constexpr bool is_sized = Sized == sized::yes; trace_iterator() = default; - constexpr explicit trace_iterator(trace& t) : trace_{&t} {} - constexpr explicit trace_iterator(int const pos, trace& t) : trace_{&t}, pos_{pos} {} + constexpr explicit trace_iterator(trace& t) noexcept(NoThrow == nothrow::yes) : trace_{&t} {} + constexpr explicit trace_iterator(int const pos, trace& t) noexcept(NoThrow == nothrow::yes) + : trace_{&t}, pos_{pos} {} trace_iterator(trace_iterator const&) requires is_forward = default; trace_iterator(trace_iterator&&) = default; - constexpr trace_iterator& operator=(trace_iterator const& that) requires is_forward { + constexpr trace_iterator& operator=(trace_iterator const& that) noexcept( + NoThrow == nothrow::yes) requires is_forward { if (!trace_) { trace_ = that.trace_; } @@ -1905,7 +1908,7 @@ namespace iter_ops { pos_ = that.pos_; return *this; } - constexpr trace_iterator& operator=(trace_iterator&& that) { + constexpr trace_iterator& operator=(trace_iterator&& that) noexcept(NoThrow == nothrow::yes) { if (!trace_) { trace_ = that.trace_; } @@ -1915,65 +1918,71 @@ namespace iter_ops { } // clang-format off - constexpr trace_iterator& operator=(default_sentinel_t) requires (Assign == assign::yes) { + constexpr trace_iterator& operator=(default_sentinel_t) noexcept(NoThrow == nothrow::yes) + requires (Assign == assign::yes) { ++trace_->assignments_; pos_ = sentinel_position; return *this; } // clang-format on - int operator*() const; + int operator*() const noexcept(NoThrow == nothrow::yes); - constexpr trace_iterator& operator++() { + constexpr trace_iterator& operator++() noexcept(NoThrow == nothrow::yes) { ++trace_->increments_; ++pos_; return *this; } - trace_iterator operator++(int); + trace_iterator operator++(int) noexcept(NoThrow == nothrow::yes); - constexpr bool operator==(default_sentinel_t) const { + constexpr bool operator==(default_sentinel_t) const noexcept(NoThrow == nothrow::yes) { ++trace_->compares_; return pos_ == sentinel_position; } - constexpr int operator-(default_sentinel_t) const requires is_sized { + constexpr int operator-(default_sentinel_t) const noexcept(NoThrow == nothrow::yes) requires is_sized { ++trace_->differences_; return pos_ - sentinel_position; } - friend constexpr int operator-(default_sentinel_t, trace_iterator const& i) requires is_sized { + friend constexpr int operator-(default_sentinel_t, trace_iterator const& i) noexcept( + NoThrow == nothrow::yes) requires is_sized { return -(i - default_sentinel); } - constexpr bool operator==(trace_iterator const& that) const requires is_forward { + constexpr bool operator==(trace_iterator const& that) const + noexcept(NoThrow == nothrow::yes) requires is_forward { ++trace_->compares_; return pos_ == that.pos_; } - constexpr trace_iterator& operator--() requires is_bidi { + constexpr trace_iterator& operator--() noexcept(NoThrow == nothrow::yes) requires is_bidi { ++trace_->decrements_; --pos_; return *this; } - trace_iterator operator--(int) requires is_bidi; + trace_iterator operator--(int) noexcept(NoThrow == nothrow::yes) requires is_bidi; - std::strong_ordering operator<=>(trace_iterator const&) const requires is_random; + std::strong_ordering operator<=>(trace_iterator const&) const + noexcept(NoThrow == nothrow::yes) requires is_random; - constexpr trace_iterator& operator+=(int const n) requires is_random { + constexpr trace_iterator& operator+=(int const n) noexcept(NoThrow == nothrow::yes) requires is_random { ++trace_->seeks_; pos_ += n; return *this; } - trace_iterator operator+(int) const requires is_random; - friend trace_iterator operator+(int, trace_iterator const&) requires is_random {} + trace_iterator operator+(int) const noexcept(NoThrow == nothrow::yes) requires is_random; + friend trace_iterator operator+(int, trace_iterator const&) noexcept( + NoThrow == nothrow::yes) requires is_random {} - trace_iterator& operator-=(int) requires is_random; - trace_iterator operator-(int) const requires is_random; + trace_iterator& operator-=(int) noexcept(NoThrow == nothrow::yes) requires is_random; + trace_iterator operator-(int) const noexcept(NoThrow == nothrow::yes) requires is_random; - constexpr int operator-(trace_iterator const& that) const requires is_random || is_sized { + constexpr int operator-(trace_iterator const& that) const + noexcept(NoThrow == nothrow::yes) requires is_random || is_sized { ++trace_->differences_; return pos_ - that.pos_; } - int operator[](int) const; + int operator[](int) const noexcept(NoThrow == nothrow::yes); trace* trace_ = nullptr; int pos_ = 0; @@ -2826,25 +2835,27 @@ namespace iter_ops { } STATIC_ASSERT(test_iter_count_sentinel_forms()); + template struct sized_test_range { mutable trace t{}; - constexpr unsigned char size() const { + constexpr unsigned char size() const noexcept(NoThrow == nothrow::yes) { ++t.sizes_; return 42; } - int* begin() const; - int* end() const; + int* begin() const noexcept(NoThrow == nothrow::yes); + int* end() const noexcept(NoThrow == nothrow::yes); }; + template struct unsized_test_range { mutable trace t{}; - constexpr trace_iterator begin() const { + constexpr trace_iterator begin() const noexcept(NoThrow == nothrow::yes) { ++t.begins_; return trace_iterator{t}; } - constexpr std::default_sentinel_t end() const { + constexpr std::default_sentinel_t end() const noexcept(NoThrow == nothrow::yes) { ++t.ends_; return {}; } @@ -2857,46 +2868,107 @@ namespace iter_ops { { // Call distance(i, s) with: sized_sentinel_for && input_iterator && last - first > 0 // Validate return is last - first - using I = trace_iterator; + using I = trace_iterator; trace t{}; - auto const result = distance(I{t}, default_sentinel); - STATIC_ASSERT(same_as const>); + I first{t}; + same_as> auto const result = distance(first, default_sentinel); + STATIC_ASSERT(!noexcept(distance(first, default_sentinel))); assert(result == sentinel_position); assert((t == trace{.differences_ = 1})); } + { + // Call distance(i, s) with: sized_sentinel_for && input_iterator && last - first > 0 + // Validate return is last - first + using I = trace_iterator; + trace t{}; + I first{t}; + same_as> auto const result = distance(first, default_sentinel); + STATIC_ASSERT(noexcept(distance(first, default_sentinel))); + assert(result == sentinel_position); + assert((t == trace{.differences_ = 1})); + } + { // Call distance(i, s) with: // sized_sentinel_for && forward_iterator && same_as && last - first < 0 // Validate return is last - first - using I = trace_iterator; + using I = trace_iterator; trace t{}; - auto const result = distance(I{sentinel_position, t}, I{t}); - STATIC_ASSERT(same_as const>); + I first{sentinel_position, t}; + I last{t}; + same_as> auto const result = distance(first, last); + STATIC_ASSERT(!noexcept(distance(first, last))); assert(result == -sentinel_position); assert((t == trace{.differences_ = 1})); } + { + // Call distance(i, s) with: + // sized_sentinel_for && forward_iterator && same_as && last - first < 0 + // Validate return is last - first + using I = trace_iterator; + trace t{}; + I first{sentinel_position, t}; + I last{t}; + same_as> auto const result = distance(first, last); + STATIC_ASSERT(noexcept(distance(first, last))); + assert(result == -sentinel_position); + assert((t == trace{.differences_ = 1})); + } + { // Call distance(i, s) with: !sized_sentinel_for && input_iterator && last - first > 0 // Validate return is last - first, and increment is called last - first times - using I = trace_iterator; + using I = trace_iterator; trace t{}; - auto const result = distance(I{t}, default_sentinel); - STATIC_ASSERT(same_as const>); + I first{t}; + same_as> auto const result = distance(move(first), default_sentinel); + STATIC_ASSERT(!noexcept(distance(move(first), default_sentinel))); assert(result == sentinel_position); assert((t == trace{.compares_ = sentinel_position + 1, .increments_ = sentinel_position})); } + { + // Call distance(i, s) with: !sized_sentinel_for && input_iterator && last - first > 0 + // Validate return is last - first, and increment is called last - first times + using I = trace_iterator; + trace t{}; + I first{t}; + same_as> auto const result = distance(move(first), default_sentinel); + STATIC_ASSERT(!noexcept(distance(move(first), default_sentinel))); // No conditional noexcept + assert(result == sentinel_position); + assert((t == trace{.compares_ = sentinel_position + 1, .increments_ = sentinel_position})); + } + + { + // Call distance(r) with: sized_range, validate that begin and end are not called + sized_test_range r{}; + same_as auto const result = distance(r); + STATIC_ASSERT(!noexcept(distance(r))); + assert(result == 42); + assert((r.t == trace{.sizes_ = 1})); + } { // Call distance(r) with: sized_range, validate that begin and end are not called - sized_test_range r{}; - auto const result = distance(r); - STATIC_ASSERT(same_as); + sized_test_range r{}; + same_as auto const result = distance(r); + STATIC_ASSERT(noexcept(distance(r))); assert(result == 42); assert((r.t == trace{.sizes_ = 1})); } + + { + // Call distance(r) with: !sized_range, validate that begin and end are called + unsized_test_range r{}; + assert(distance(r) == sentinel_position); + STATIC_ASSERT(!noexcept(distance(r))); + trace const expected{ + .compares_ = sentinel_position + 1, .increments_ = sentinel_position, .begins_ = 1, .ends_ = 1}; + assert(r.t == expected); + } { // Call distance(r) with: !sized_range, validate that begin and end are called - unsized_test_range r{}; + unsized_test_range r{}; assert(distance(r) == sentinel_position); + STATIC_ASSERT(!noexcept(distance(r))); // No conditional noexcept trace const expected{ .compares_ = sentinel_position + 1, .increments_ = sentinel_position, .begins_ = 1, .ends_ = 1}; assert(r.t == expected);