Skip to content

Commit 16bb556

Browse files
JMazurkiewiczStephanTLavavejCaseyCarter
authored
P2164R9: views::enumerate (#3472)
Co-authored-by: Stephan T. Lavavej <[email protected]> Co-authored-by: Casey Carter <[email protected]>
1 parent 9a0f5b6 commit 16bb556

File tree

6 files changed

+1039
-2
lines changed

6 files changed

+1039
-2
lines changed

stl/inc/ranges

Lines changed: 334 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5919,6 +5919,338 @@ namespace ranges {
59195919
} // namespace views
59205920

59215921
#if _HAS_CXX23
5922+
template <class _Rng>
5923+
concept _Range_with_movable_references = input_range<_Rng> && move_constructible<range_reference_t<_Rng>>
5924+
&& move_constructible<range_rvalue_reference_t<_Rng>>;
5925+
5926+
_EXPORT_STD template <view _Vw>
5927+
requires _Range_with_movable_references<_Vw>
5928+
class enumerate_view : public view_interface<enumerate_view<_Vw>> {
5929+
private:
5930+
/* [[no_unique_address]] */ _Vw _Range{};
5931+
5932+
template <bool _Const>
5933+
class _Iterator {
5934+
private:
5935+
friend enumerate_view;
5936+
5937+
using _Base_t = _Maybe_const<_Const, _Vw>;
5938+
using _Base_iterator = iterator_t<_Base_t>;
5939+
using _Reference_type = tuple<range_difference_t<_Base_t>, range_reference_t<_Base_t>>;
5940+
5941+
/* [[no_unique_address]] */ _Base_iterator _Current{};
5942+
range_difference_t<_Base_t> _Pos = 0;
5943+
5944+
constexpr explicit _Iterator(_Base_iterator _Current_, range_difference_t<_Base_t> _Pos_) noexcept(
5945+
is_nothrow_move_constructible_v<_Base_iterator>) // strengthened
5946+
: _Current(_STD move(_Current_)), _Pos(_Pos_) {}
5947+
5948+
public:
5949+
using iterator_category = input_iterator_tag;
5950+
using iterator_concept = conditional_t<random_access_range<_Base_t>, random_access_iterator_tag,
5951+
conditional_t<bidirectional_range<_Base_t>, bidirectional_iterator_tag,
5952+
conditional_t<forward_range<_Base_t>, forward_iterator_tag, input_iterator_tag>>>;
5953+
using difference_type = range_difference_t<_Base_t>;
5954+
using value_type = tuple<difference_type, range_value_t<_Base_t>>;
5955+
5956+
// clang-format off
5957+
_Iterator() requires default_initializable<_Base_iterator> = default;
5958+
// clang-format on
5959+
5960+
constexpr _Iterator(_Iterator<!_Const> _Other) noexcept(
5961+
is_nothrow_constructible_v<_Base_iterator, iterator_t<_Vw>>) // strengthened
5962+
requires _Const && convertible_to<iterator_t<_Vw>, _Base_iterator>
5963+
: _Current(_STD move(_Other._Current)), _Pos(_Other._Pos) {}
5964+
5965+
_NODISCARD constexpr const _Base_iterator& base() const& noexcept {
5966+
return _Current;
5967+
}
5968+
5969+
_NODISCARD constexpr _Base_iterator base() && noexcept(
5970+
is_nothrow_move_constructible_v<_Base_iterator>) /* strengthened */ {
5971+
return _STD move(_Current);
5972+
}
5973+
5974+
_NODISCARD constexpr difference_type index() const noexcept {
5975+
return _Pos;
5976+
}
5977+
5978+
_NODISCARD constexpr auto operator*() const noexcept(
5979+
noexcept(*_Current) && is_nothrow_copy_constructible_v<range_reference_t<_Base_t>>) /* strengthened */ {
5980+
return _Reference_type{_Pos, *_Current};
5981+
}
5982+
5983+
constexpr _Iterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ {
5984+
++_Current;
5985+
++_Pos;
5986+
return *this;
5987+
}
5988+
5989+
constexpr void operator++(int) noexcept(noexcept(++_Current)) /* strengthened */ {
5990+
++*this;
5991+
}
5992+
5993+
constexpr _Iterator operator++(int) noexcept(
5994+
noexcept(++*this) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened
5995+
requires forward_range<_Base_t>
5996+
{
5997+
auto _Tmp = *this;
5998+
++*this;
5999+
return _Tmp;
6000+
}
6001+
6002+
constexpr _Iterator& operator--() noexcept(noexcept(--_Current)) // strengthened
6003+
requires bidirectional_range<_Base_t>
6004+
{
6005+
--_Current;
6006+
--_Pos;
6007+
return *this;
6008+
}
6009+
6010+
constexpr _Iterator operator--(int) noexcept(
6011+
noexcept(--*this) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened
6012+
requires bidirectional_range<_Base_t>
6013+
{
6014+
auto _Tmp = *this;
6015+
--*this;
6016+
return _Tmp;
6017+
}
6018+
6019+
constexpr _Iterator& operator+=(const difference_type _Off) noexcept(
6020+
noexcept(_Current += _Off)) // strengthened
6021+
requires random_access_range<_Base_t>
6022+
{
6023+
_Current += _Off;
6024+
_Pos += _Off;
6025+
return *this;
6026+
}
6027+
6028+
constexpr _Iterator& operator-=(const difference_type _Off) noexcept(
6029+
noexcept(_Current -= _Off)) // strengthened
6030+
requires random_access_range<_Base_t>
6031+
{
6032+
_Current -= _Off;
6033+
_Pos -= _Off;
6034+
return *this;
6035+
}
6036+
6037+
_NODISCARD constexpr auto operator[](const difference_type _Off) const noexcept(
6038+
noexcept(_Current[_Off]) && is_nothrow_copy_constructible_v<range_reference_t<_Base_t>>) // strengthened
6039+
requires random_access_range<_Base_t>
6040+
{
6041+
return _Reference_type{static_cast<difference_type>(_Pos + _Off), _Current[_Off]};
6042+
}
6043+
6044+
_NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept {
6045+
return _Left._Pos == _Right._Pos;
6046+
}
6047+
6048+
_NODISCARD_FRIEND constexpr strong_ordering operator<=>(
6049+
const _Iterator& _Left, const _Iterator& _Right) noexcept {
6050+
return _Left._Pos <=> _Right._Pos;
6051+
}
6052+
6053+
_NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _It, const difference_type _Off) noexcept(
6054+
is_nothrow_copy_constructible_v<_Iterator>&& noexcept(
6055+
_STD declval<_Iterator&>() += _Off)) // strengthened
6056+
requires random_access_range<_Base_t>
6057+
{
6058+
auto _Tmp = _It;
6059+
_Tmp += _Off;
6060+
return _Tmp;
6061+
}
6062+
6063+
_NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Off, const _Iterator& _It) noexcept(
6064+
noexcept(_It + _Off)) // strengthened
6065+
requires random_access_range<_Base_t>
6066+
{
6067+
return _It + _Off;
6068+
}
6069+
6070+
_NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, const difference_type _Off) noexcept(
6071+
is_nothrow_copy_constructible_v<_Iterator>&& noexcept(
6072+
_STD declval<_Iterator&>() -= _Off)) // strengthened
6073+
requires random_access_range<_Base_t>
6074+
{
6075+
auto _Tmp = _It;
6076+
_Tmp -= _Off;
6077+
return _Tmp;
6078+
}
6079+
6080+
_NODISCARD_FRIEND constexpr difference_type operator-(
6081+
const _Iterator& _Left, const _Iterator& _Right) noexcept /* strengthened */ {
6082+
return _Left._Pos - _Right._Pos;
6083+
}
6084+
6085+
_NODISCARD_FRIEND constexpr auto iter_move(const _Iterator& _It) noexcept(
6086+
noexcept(_RANGES iter_move(_It._Current))
6087+
&& is_nothrow_move_constructible_v<range_rvalue_reference_t<_Base_t>>) {
6088+
return tuple<difference_type, range_rvalue_reference_t<_Base_t>>{
6089+
_It._Pos, _RANGES iter_move(_It._Current)};
6090+
}
6091+
};
6092+
6093+
template <bool _Const>
6094+
class _Sentinel {
6095+
private:
6096+
friend enumerate_view;
6097+
6098+
using _Base_t = _Maybe_const<_Const, _Vw>;
6099+
using _Base_sentinel = sentinel_t<_Base_t>;
6100+
6101+
/* [[no_unique_address]] */ _Base_sentinel _End{};
6102+
6103+
constexpr explicit _Sentinel(_Base_sentinel _End_) noexcept(
6104+
is_nothrow_move_constructible_v<_Base_sentinel>) // strengthened
6105+
: _End(_STD move(_End_)) {}
6106+
6107+
template <bool _OtherConst>
6108+
requires sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>>
6109+
_NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const
6110+
noexcept(noexcept(_Fake_copy_init<bool>(_It._Current == _End))) {
6111+
return _It._Current == _End;
6112+
}
6113+
6114+
template <bool _OtherConst>
6115+
requires sized_sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>>
6116+
_NODISCARD constexpr range_difference_t<_Maybe_const<_OtherConst, _Vw>> _Distance_from(
6117+
const _Iterator<_OtherConst>& _It) const noexcept(noexcept(_End - _It._Current)) {
6118+
return _End - _It._Current;
6119+
}
6120+
6121+
public:
6122+
_Sentinel() = default;
6123+
6124+
constexpr _Sentinel(_Sentinel<!_Const> _Other) noexcept(
6125+
is_nothrow_constructible_v<_Base_sentinel, sentinel_t<_Vw>>) // strengthened
6126+
requires _Const && convertible_to<sentinel_t<_Vw>, _Base_sentinel>
6127+
: _End(_STD move(_Other._End)) {}
6128+
6129+
_NODISCARD constexpr _Base_sentinel base() const
6130+
noexcept(is_nothrow_copy_constructible_v<_Base_sentinel>) /* strengthened */ {
6131+
return _End;
6132+
}
6133+
6134+
template <bool _OtherConst>
6135+
requires sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>>
6136+
_NODISCARD_FRIEND constexpr bool operator==(const _Iterator<_OtherConst>& _It, const _Sentinel& _Se) //
6137+
noexcept(noexcept(_Se._Equal(_It))) /* strengthened */ {
6138+
return _Se._Equal(_It);
6139+
}
6140+
6141+
template <bool _OtherConst>
6142+
requires sized_sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>>
6143+
_NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Vw>> operator-(
6144+
const _Iterator<_OtherConst>& _It, const _Sentinel& _Se) //
6145+
noexcept(noexcept(_Se._Distance_from(_It))) /* strengthened */ {
6146+
return -_Se._Distance_from(_It);
6147+
}
6148+
6149+
template <bool _OtherConst>
6150+
requires sized_sentinel_for<_Base_sentinel, iterator_t<_Maybe_const<_OtherConst, _Vw>>>
6151+
_NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Vw>> operator-(
6152+
const _Sentinel& _Se, const _Iterator<_OtherConst>& _It) //
6153+
noexcept(noexcept(_Se._Distance_from(_It))) /* strengthened */ {
6154+
return _Se._Distance_from(_It);
6155+
}
6156+
};
6157+
6158+
template <class _Rng>
6159+
static constexpr bool _Is_end_nothrow_v = is_nothrow_move_constructible_v<sentinel_t<_Rng>> //
6160+
&& noexcept(_RANGES end(_STD declval<_Rng&>()));
6161+
6162+
template <class _Rng>
6163+
requires common_range<_Rng> && sized_range<_Rng>
6164+
static constexpr bool _Is_end_nothrow_v<_Rng> = is_nothrow_move_constructible_v<sentinel_t<_Rng>> //
6165+
&& noexcept(_RANGES end(_STD declval<_Rng&>())) //
6166+
&& noexcept(_RANGES distance(_STD declval<_Rng&>()));
6167+
6168+
public:
6169+
// clang-format off
6170+
constexpr enumerate_view() requires default_initializable<_Vw> = default;
6171+
// clang-format on
6172+
6173+
constexpr explicit enumerate_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened
6174+
: _Range(_STD move(_Range_)) {}
6175+
6176+
_NODISCARD constexpr auto begin() noexcept(
6177+
noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v<iterator_t<_Vw>>) // strengthened
6178+
requires (!_Simple_view<_Vw>)
6179+
{
6180+
return _Iterator<false>{_RANGES begin(_Range), 0};
6181+
}
6182+
6183+
_NODISCARD constexpr auto begin() const noexcept(
6184+
noexcept(_RANGES begin(_Range)) && is_nothrow_move_constructible_v<iterator_t<const _Vw>>) // strengthened
6185+
requires _Range_with_movable_references<const _Vw>
6186+
{
6187+
return _Iterator<true>{_RANGES begin(_Range), 0};
6188+
}
6189+
6190+
_NODISCARD constexpr auto end() noexcept(_Is_end_nothrow_v<_Vw>) // strengthened
6191+
requires (!_Simple_view<_Vw>)
6192+
{
6193+
if constexpr (common_range<_Vw> && sized_range<_Vw>) {
6194+
return _Iterator<false>{_RANGES end(_Range), _RANGES distance(_Range)};
6195+
} else {
6196+
return _Sentinel<false>{_RANGES end(_Range)};
6197+
}
6198+
}
6199+
6200+
_NODISCARD constexpr auto end() const noexcept(_Is_end_nothrow_v<const _Vw>) // strengthened
6201+
requires _Range_with_movable_references<const _Vw>
6202+
{
6203+
if constexpr (common_range<const _Vw> && sized_range<const _Vw>) {
6204+
return _Iterator<true>{_RANGES end(_Range), _RANGES distance(_Range)};
6205+
} else {
6206+
return _Sentinel<true>{_RANGES end(_Range)};
6207+
}
6208+
}
6209+
6210+
_NODISCARD constexpr auto size() noexcept(noexcept(_RANGES size(_Range))) // strengthened
6211+
requires sized_range<_Vw>
6212+
{
6213+
return _RANGES size(_Range);
6214+
}
6215+
6216+
_NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(_Range))) // strengthened
6217+
requires sized_range<const _Vw>
6218+
{
6219+
return _RANGES size(_Range);
6220+
}
6221+
6222+
_NODISCARD constexpr _Vw base() const& noexcept(is_nothrow_copy_constructible_v<_Vw>) // strengthened
6223+
requires copy_constructible<_Vw>
6224+
{
6225+
return _Range;
6226+
}
6227+
6228+
_NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ {
6229+
return _STD move(_Range);
6230+
}
6231+
};
6232+
6233+
template <class _Rng>
6234+
enumerate_view(_Rng&&) -> enumerate_view<views::all_t<_Rng>>;
6235+
6236+
template <class _Rng>
6237+
inline constexpr bool enable_borrowed_range<enumerate_view<_Rng>> = enable_borrowed_range<_Rng>;
6238+
6239+
namespace views {
6240+
class _Enumerate_fn : public _Pipe::_Base<_Enumerate_fn> {
6241+
public:
6242+
template <viewable_range _Rng>
6243+
_NODISCARD constexpr auto operator()(_Rng&& _Range) const
6244+
noexcept(noexcept(enumerate_view<views::all_t<_Rng>>{_STD forward<_Rng>(_Range)}))
6245+
requires requires { enumerate_view<views::all_t<_Rng>>{_STD forward<_Rng>(_Range)}; }
6246+
{
6247+
return enumerate_view<views::all_t<_Rng>>{_STD forward<_Rng>(_Range)};
6248+
}
6249+
};
6250+
6251+
_EXPORT_STD inline constexpr _Enumerate_fn enumerate;
6252+
} // namespace views
6253+
59226254
template <class _Size>
59236255
_NODISCARD constexpr _Size _Div_ceil(const _Size _Num, const _Size _Denom) noexcept {
59246256
_Size _Result = _Num / _Denom;
@@ -6573,8 +6905,8 @@ namespace ranges {
65736905

65746906
_Iterator() = default;
65756907

6576-
constexpr _Iterator(_Iterator<!_Const> _Other) noexcept(is_nothrow_constructible_v<_Base_iterator,
6577-
typename _Iterator<!_Const>::_Base_iterator>) /* strengthened */
6908+
constexpr _Iterator(_Iterator<!_Const> _Other) noexcept(
6909+
is_nothrow_constructible_v<_Base_iterator, iterator_t<_Vw>>) // strengthened
65786910
requires _Const && convertible_to<iterator_t<_Vw>, _Base_iterator>
65796911
: _Current(_STD move(_Other._Current)), _Count(_Other._Count) {}
65806912

stl/inc/yvals_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@
324324
// P1989R2 Range Constructor For string_view
325325
// P2077R3 Heterogeneous Erasure Overloads For Associative Containers
326326
// P2136R3 invoke_r()
327+
// P2164R9 views::enumerate
327328
// P2165R4 Compatibility Between tuple, pair, And tuple-like Objects
328329
// P2166R1 Prohibiting basic_string And basic_string_view Construction From nullptr
329330
// P2186R2 Removing Garbage Collection Support
@@ -1720,6 +1721,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
17201721
#define __cpp_lib_ranges_chunk 202202L
17211722
#define __cpp_lib_ranges_chunk_by 202202L
17221723
#define __cpp_lib_ranges_contains 202207L
1724+
#define __cpp_lib_ranges_enumerate 202302L
17231725
#define __cpp_lib_ranges_find_last 202207L
17241726
#define __cpp_lib_ranges_fold 202207L
17251727
#define __cpp_lib_ranges_iota 202202L

tests/std/test.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ tests\P1899R3_views_stride_death
551551
tests\P1951R1_default_arguments_pair_forward_ctor
552552
tests\P2136R3_invoke_r
553553
tests\P2162R2_std_visit_for_derived_classes_from_variant
554+
tests\P2164R9_views_enumerate
554555
tests\P2165R4_tuple_like_common_reference
555556
tests\P2165R4_tuple_like_common_type
556557
tests\P2165R4_tuple_like_operations
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3+
4+
RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst

0 commit comments

Comments
 (0)