Skip to content

Commit bbd5dba

Browse files
Speculatively implement LWG-3664 (#2522)
to fix a regression when calling `ranges::distance` with array arguments which _should_ decay to pointers. While we're here, let's merge `distance(i, s)` overloads and use `if constexpr` dispatch instead of concept overloading. Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent ee6a79e commit bbd5dba

File tree

2 files changed

+63
-13
lines changed

2 files changed

+63
-13
lines changed

stl/inc/xutility

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,19 +2868,16 @@ namespace ranges {
28682868
public:
28692869
using _Not_quite_object::_Not_quite_object;
28702870

2871-
// clang-format off
2872-
template <input_or_output_iterator _It, sentinel_for<_It> _Se>
2873-
requires (!sized_sentinel_for<_Se, _It>)
2874-
_NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const {
2875-
// clang-format on
2876-
_Adl_verify_range(_First, _Last);
2877-
return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)));
2878-
}
2879-
2880-
template <input_or_output_iterator _It, sized_sentinel_for<_It> _Se>
2881-
_NODISCARD constexpr iter_difference_t<_It> operator()(const _It& _First, const _Se& _Last) const
2882-
noexcept(noexcept(_Last - _First)) /* strengthened */ {
2883-
return _Last - _First;
2871+
template <class _It, sentinel_for<decay_t<_It>> _Se>
2872+
_NODISCARD constexpr iter_difference_t<decay_t<_It>> operator()(_It&& _Raw_first, _Se _Last) const
2873+
noexcept(_Nothrow_dist<_It, _Se>) /* strengthened */ {
2874+
if constexpr (sized_sentinel_for<_Se, decay_t<_It>>) {
2875+
return _Last - static_cast<const decay_t<_It>&>(_Raw_first); // Per LWG-3664
2876+
} else {
2877+
auto _First = _STD forward<_It>(_Raw_first);
2878+
_Adl_verify_range(_First, _Last);
2879+
return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)));
2880+
}
28842881
}
28852882

28862883
template <range _Rng>
@@ -2894,6 +2891,13 @@ namespace ranges {
28942891
}
28952892

28962893
private:
2894+
template <class _It, class _Se>
2895+
static constexpr bool _Nothrow_dist = false;
2896+
2897+
template <class _It, sized_sentinel_for<decay_t<_It>> _Se>
2898+
static constexpr bool _Nothrow_dist<_It, _Se> = noexcept(
2899+
_STD declval<_Se&>() - _STD declval<const decay_t<_It>&>());
2900+
28972901
template <class _It, class _Se>
28982902
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept(
28992903
noexcept(++_First != _Last)) {

tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2861,6 +2861,32 @@ namespace iter_ops {
28612861
}
28622862
};
28632863

2864+
template <class Element>
2865+
struct pointer_sentinel {
2866+
Element* ptr = nullptr;
2867+
2868+
pointer_sentinel() = default;
2869+
constexpr explicit pointer_sentinel(Element* const p) noexcept : ptr{p} {}
2870+
2871+
template <class T>
2872+
[[nodiscard]] constexpr bool operator==(T* that) const noexcept {
2873+
static_assert(std::same_as<T, Element>);
2874+
return ptr == that;
2875+
}
2876+
2877+
template <class T>
2878+
[[nodiscard]] friend constexpr std::ptrdiff_t operator-(T* x, const pointer_sentinel& y) noexcept {
2879+
static_assert(std::same_as<T, Element>);
2880+
return x - y.ptr;
2881+
}
2882+
2883+
template <class T>
2884+
[[nodiscard]] friend constexpr std::ptrdiff_t operator-(const pointer_sentinel& y, T* x) noexcept {
2885+
static_assert(std::same_as<T, Element>);
2886+
return y.ptr - x;
2887+
}
2888+
};
2889+
28642890
constexpr bool test_distance() {
28652891
using ranges::distance, ranges::size;
28662892
using std::iter_difference_t, std::same_as;
@@ -2974,6 +3000,26 @@ namespace iter_ops {
29743000
assert(r.t == expected);
29753001
}
29763002

3003+
{
3004+
// Call distance(i, s) with arrays which must be decayed to pointers.
3005+
// (This behavior was regressed by LWG-3392.)
3006+
int some_ints[] = {1, 2, 3};
3007+
assert(distance(some_ints, pointer_sentinel{some_ints + 1}) == 1);
3008+
STATIC_ASSERT(noexcept(distance(some_ints, pointer_sentinel{some_ints + 1})));
3009+
assert(distance(some_ints + 1, some_ints) == -1);
3010+
STATIC_ASSERT(noexcept(distance(some_ints + 1, some_ints)));
3011+
assert(distance(some_ints, some_ints) == 0);
3012+
STATIC_ASSERT(noexcept(distance(some_ints, some_ints)));
3013+
3014+
const auto& const_ints = some_ints;
3015+
assert(distance(const_ints, pointer_sentinel{const_ints + 1}) == 1);
3016+
STATIC_ASSERT(noexcept(distance(const_ints, pointer_sentinel{const_ints + 1})));
3017+
assert(distance(const_ints + 1, const_ints) == -1);
3018+
STATIC_ASSERT(noexcept(distance(const_ints + 1, const_ints)));
3019+
assert(distance(const_ints, const_ints) == 0);
3020+
STATIC_ASSERT(noexcept(distance(const_ints, const_ints)));
3021+
}
3022+
29773023
return true;
29783024
}
29793025
STATIC_ASSERT(test_distance());

0 commit comments

Comments
 (0)