зеркало из https://github.com/microsoft/STL.git
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:
Родитель
ee6a79e4ca
Коммит
bbd5dbaa61
|
@ -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
|
||||
_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 <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 <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());
|
||||
|
|
Загрузка…
Ссылка в новой задаче