зеркало из 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:
|
public:
|
||||||
using _Not_quite_object::_Not_quite_object;
|
using _Not_quite_object::_Not_quite_object;
|
||||||
|
|
||||||
// clang-format off
|
template <class _It, sentinel_for<decay_t<_It>> _Se>
|
||||||
template <input_or_output_iterator _It, sentinel_for<_It> _Se>
|
_NODISCARD constexpr iter_difference_t<decay_t<_It>> operator()(_It&& _Raw_first, _Se _Last) const
|
||||||
requires (!sized_sentinel_for<_Se, _It>)
|
noexcept(_Nothrow_dist<_It, _Se>) /* strengthened */ {
|
||||||
_NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const {
|
if constexpr (sized_sentinel_for<_Se, decay_t<_It>>) {
|
||||||
// clang-format on
|
return _Last - static_cast<const decay_t<_It>&>(_Raw_first); // Per LWG-3664
|
||||||
_Adl_verify_range(_First, _Last);
|
} else {
|
||||||
return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)));
|
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>
|
template <range _Rng>
|
||||||
|
@ -2894,6 +2891,13 @@ namespace ranges {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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>
|
template <class _It, class _Se>
|
||||||
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept(
|
_NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept(
|
||||||
noexcept(++_First != _Last)) {
|
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() {
|
constexpr bool test_distance() {
|
||||||
using ranges::distance, ranges::size;
|
using ranges::distance, ranges::size;
|
||||||
using std::iter_difference_t, std::same_as;
|
using std::iter_difference_t, std::same_as;
|
||||||
|
@ -2974,6 +3000,26 @@ namespace iter_ops {
|
||||||
assert(r.t == expected);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
STATIC_ASSERT(test_distance());
|
STATIC_ASSERT(test_distance());
|
||||||
|
|
Загрузка…
Ссылка в новой задаче