diff --git a/stl/inc/xutility b/stl/inc/xutility index 9eb999a7e..b52cd4281 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2868,19 +2868,16 @@ namespace ranges { public: using _Not_quite_object::_Not_quite_object; - // clang-format off - template _Se> - 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 > _Se> + _NODISCARD constexpr iter_difference_t> operator()(_It&& _Raw_first, _Se _Last) const + noexcept(_Nothrow_dist<_It, _Se>) /* strengthened */ { + if constexpr (sized_sentinel_for<_Se, decay_t<_It>>) { + return _Last - static_cast&>(_Raw_first); // Per LWG-3664 + } else { + auto _First = _STD forward<_It>(_Raw_first); + _Adl_verify_range(_First, _Last); + return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); + } } template @@ -2894,6 +2891,13 @@ namespace ranges { } private: + template + static constexpr bool _Nothrow_dist = false; + + template > _Se> + static constexpr bool _Nothrow_dist<_It, _Se> = noexcept( + _STD declval<_Se&>() - _STD declval&>()); + template _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept( noexcept(++_First != _Last)) { diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 1479dc3c5..41d766f62 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -2861,6 +2861,32 @@ namespace iter_ops { } }; + template + struct pointer_sentinel { + Element* ptr = nullptr; + + pointer_sentinel() = default; + constexpr explicit pointer_sentinel(Element* const p) noexcept : ptr{p} {} + + template + [[nodiscard]] constexpr bool operator==(T* that) const noexcept { + static_assert(std::same_as); + return ptr == that; + } + + template + [[nodiscard]] friend constexpr std::ptrdiff_t operator-(T* x, const pointer_sentinel& y) noexcept { + static_assert(std::same_as); + return x - y.ptr; + } + + template + [[nodiscard]] friend constexpr std::ptrdiff_t operator-(const pointer_sentinel& y, T* x) noexcept { + static_assert(std::same_as); + return y.ptr - x; + } + }; + constexpr bool test_distance() { using ranges::distance, ranges::size; using std::iter_difference_t, std::same_as; @@ -2974,6 +3000,26 @@ namespace iter_ops { assert(r.t == expected); } + { + // Call distance(i, s) with arrays which must be decayed to pointers. + // (This behavior was regressed by LWG-3392.) + int some_ints[] = {1, 2, 3}; + assert(distance(some_ints, pointer_sentinel{some_ints + 1}) == 1); + STATIC_ASSERT(noexcept(distance(some_ints, pointer_sentinel{some_ints + 1}))); + assert(distance(some_ints + 1, some_ints) == -1); + STATIC_ASSERT(noexcept(distance(some_ints + 1, some_ints))); + assert(distance(some_ints, some_ints) == 0); + STATIC_ASSERT(noexcept(distance(some_ints, some_ints))); + + const auto& const_ints = some_ints; + assert(distance(const_ints, pointer_sentinel{const_ints + 1}) == 1); + STATIC_ASSERT(noexcept(distance(const_ints, pointer_sentinel{const_ints + 1}))); + assert(distance(const_ints + 1, const_ints) == -1); + STATIC_ASSERT(noexcept(distance(const_ints + 1, const_ints))); + assert(distance(const_ints, const_ints) == 0); + STATIC_ASSERT(noexcept(distance(const_ints, const_ints))); + } + return true; } STATIC_ASSERT(test_distance());