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 <stl@nuwen.net>
This commit is contained in:
Casey Carter 2022-02-07 11:54:49 -08:00 коммит произвёл GitHub
Родитель ee6a79e4ca
Коммит bbd5dbaa61
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 63 добавлений и 13 удалений

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

@ -2868,19 +2868,16 @@ namespace ranges {
public:
using _Not_quite_object::_Not_quite_object;
// clang-format off
template <input_or_output_iterator _It, sentinel_for<_It> _Se>
requires (!sized_sentinel_for<_Se, _It>)
_NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const {
// clang-format on
template <class _It, sentinel_for<decay_t<_It>> _Se>
_NODISCARD constexpr iter_difference_t<decay_t<_It>> 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<const decay_t<_It>&>(_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 <input_or_output_iterator _It, sized_sentinel_for<_It> _Se>
_NODISCARD constexpr iter_difference_t<_It> operator()(const _It& _First, const _Se& _Last) const
noexcept(noexcept(_Last - _First)) /* strengthened */ {
return _Last - _First;
}
template <range _Rng>
@ -2894,6 +2891,13 @@ namespace ranges {
}
private:
template <class _It, class _Se>
static constexpr bool _Nothrow_dist = false;
template <class _It, sized_sentinel_for<decay_t<_It>> _Se>
static constexpr bool _Nothrow_dist<_It, _Se> = noexcept(
_STD declval<_Se&>() - _STD declval<const decay_t<_It>&>());
template <class _It, class _Se>
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept(
noexcept(++_First != _Last)) {

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

@ -2861,6 +2861,32 @@ namespace iter_ops {
}
};
template <class Element>
struct pointer_sentinel {
Element* ptr = nullptr;
pointer_sentinel() = default;
constexpr explicit pointer_sentinel(Element* const p) noexcept : ptr{p} {}
template <class T>
[[nodiscard]] constexpr bool operator==(T* that) const noexcept {
static_assert(std::same_as<T, Element>);
return ptr == that;
}
template <class T>
[[nodiscard]] friend constexpr std::ptrdiff_t operator-(T* x, const pointer_sentinel& y) noexcept {
static_assert(std::same_as<T, Element>);
return x - y.ptr;
}
template <class T>
[[nodiscard]] friend constexpr std::ptrdiff_t operator-(const pointer_sentinel& y, T* x) noexcept {
static_assert(std::same_as<T, Element>);
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());