Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

operations.hpp 22KB

il y a 5 ans
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Eric Niebler 2014-present
  5. //
  6. // Use, modification and distribution is subject to the
  7. // Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. // Project home: https://github.com/ericniebler/range-v3
  12. //
  13. #ifndef RANGES_V3_ITERATOR_OPERATIONS_HPP
  14. #define RANGES_V3_ITERATOR_OPERATIONS_HPP
  15. #include <type_traits>
  16. #include <utility>
  17. #include <range/v3/range_fwd.hpp>
  18. #include <range/v3/iterator/concepts.hpp>
  19. #include <range/v3/iterator/traits.hpp>
  20. #include <range/v3/range/concepts.hpp>
  21. namespace ranges
  22. {
  23. /// \addtogroup group-iterator
  24. /// @{
  25. /// \cond
  26. CPP_template(typename I)( //
  27. requires input_or_output_iterator<I>) //
  28. struct counted_iterator;
  29. /// \endcond
  30. struct advance_fn
  31. {
  32. #if RANGES_CXX_IF_CONSTEXPR >= RANGES_CXX_IF_CONSTEXPR_17
  33. template<typename I>
  34. constexpr auto operator()(I & i, iter_difference_t<I> n) const
  35. -> CPP_ret(void)( //
  36. requires input_or_output_iterator<I>)
  37. // [[expects: n >= 0 || bidirectional_iterator<I>]]
  38. {
  39. if constexpr(random_access_iterator<I>)
  40. {
  41. i += n;
  42. }
  43. else
  44. {
  45. if constexpr(bidirectional_iterator<I>)
  46. for(; 0 > n; ++n)
  47. --i;
  48. RANGES_EXPECT(0 <= n);
  49. for(; 0 < n; --n)
  50. ++i;
  51. }
  52. }
  53. template<typename I, typename S>
  54. constexpr auto operator()(I & i, S bound) const -> CPP_ret(void)( //
  55. requires sentinel_for<S, I>)
  56. // [[expects axiom: reachable(i, bound)]]
  57. {
  58. if constexpr(assignable_from<I &, S>)
  59. {
  60. i = std::move(bound);
  61. }
  62. else if constexpr(sized_sentinel_for<S, I>)
  63. {
  64. iter_difference_t<I> d = bound - i;
  65. RANGES_EXPECT(0 <= d);
  66. (*this)(i, d);
  67. }
  68. else
  69. while(i != bound)
  70. ++i;
  71. }
  72. template<typename I, typename S>
  73. constexpr auto operator()(I & i, iter_difference_t<I> n, S bound) const
  74. -> CPP_ret(iter_difference_t<I>)( //
  75. requires sentinel_for<S, I>)
  76. // [[expects axiom: 0 == n ||
  77. // (0 < n && reachable(i, bound)) ||
  78. // (0 > n && same_as<I, S> && bidirectional_iterator<I> && reachable(bound,
  79. // i))]]
  80. {
  81. if constexpr(sized_sentinel_for<S, I>)
  82. {
  83. if(0 == n)
  84. return 0;
  85. const auto d = bound - i;
  86. if constexpr(bidirectional_iterator<I> && same_as<I, S>)
  87. {
  88. RANGES_EXPECT(0 <= n ? 0 <= d : 0 >= d);
  89. if(0 <= n ? d <= n : d >= n)
  90. {
  91. i = std::move(bound);
  92. return n - d;
  93. }
  94. }
  95. else
  96. {
  97. RANGES_EXPECT(0 <= n && 0 <= d);
  98. if(d <= n)
  99. {
  100. (*this)(i, std::move(bound));
  101. return n - d;
  102. }
  103. }
  104. (*this)(i, n);
  105. return 0;
  106. }
  107. else
  108. {
  109. if constexpr(bidirectional_iterator<I> && same_as<I, S>)
  110. {
  111. if(0 > n)
  112. {
  113. do
  114. {
  115. --i;
  116. ++n;
  117. } while(0 != n && i != bound);
  118. return n;
  119. }
  120. }
  121. RANGES_EXPECT(0 <= n);
  122. while(0 != n && i != bound)
  123. {
  124. ++i;
  125. --n;
  126. }
  127. return n;
  128. }
  129. }
  130. #else
  131. private:
  132. template<typename I>
  133. static constexpr void n_(I & i, iter_difference_t<I> n,
  134. detail::input_iterator_tag_);
  135. template<typename I>
  136. static constexpr void n_(I & i, iter_difference_t<I> n,
  137. detail::bidirectional_iterator_tag_);
  138. template<typename I>
  139. static constexpr void n_(I & i, iter_difference_t<I> n,
  140. detail::random_access_iterator_tag_);
  141. template<typename I, typename S>
  142. static constexpr void to_impl_(I & i, S s, sentinel_tag);
  143. template<typename I, typename S>
  144. static constexpr void to_impl_(I & i, S s, sized_sentinel_tag);
  145. template<typename I, typename S>
  146. static constexpr void to_(I & i, S s, std::true_type); // assignable
  147. template<typename I, typename S>
  148. static constexpr void to_(I & i, S s, std::false_type); // !assignable
  149. template<typename I, typename S>
  150. static constexpr iter_difference_t<I> bounded_(I & it, iter_difference_t<I> n,
  151. S bound, sentinel_tag,
  152. detail::input_iterator_tag_);
  153. template<typename I>
  154. static constexpr iter_difference_t<I> bounded_(
  155. I & it, iter_difference_t<I> n, I bound, sentinel_tag,
  156. detail::bidirectional_iterator_tag_);
  157. template<typename I, typename S, typename Concept>
  158. static constexpr iter_difference_t<I> bounded_(I & it, iter_difference_t<I> n,
  159. S bound, sized_sentinel_tag,
  160. Concept);
  161. public:
  162. // Advance a certain number of steps:
  163. template<typename I>
  164. constexpr auto operator()(I & i, iter_difference_t<I> n) const
  165. -> CPP_ret(void)( //
  166. requires input_or_output_iterator<I>)
  167. {
  168. advance_fn::n_(i, n, iterator_tag_of<I>{});
  169. }
  170. // Advance to a certain position:
  171. template<typename I, typename S>
  172. constexpr auto operator()(I & i, S s) const -> CPP_ret(void)( //
  173. requires sentinel_for<S, I>)
  174. {
  175. advance_fn::to_(
  176. i, static_cast<S &&>(s), meta::bool_<assignable_from<I &, S>>());
  177. }
  178. // Advance a certain number of times, with a bound:
  179. template<typename I, typename S>
  180. constexpr auto operator()(I & it, iter_difference_t<I> n, S bound) const
  181. -> CPP_ret(iter_difference_t<I>)( //
  182. requires sentinel_for<S, I>)
  183. {
  184. return advance_fn::bounded_(it,
  185. n,
  186. static_cast<S &&>(bound),
  187. sentinel_tag_of<S, I>(),
  188. iterator_tag_of<I>());
  189. }
  190. #endif
  191. template<typename I>
  192. constexpr auto operator()(counted_iterator<I> & i, iter_difference_t<I> n) const
  193. -> CPP_ret(void)( //
  194. requires input_or_output_iterator<I>);
  195. };
  196. /// \sa `advance_fn`
  197. RANGES_INLINE_VARIABLE(advance_fn, advance)
  198. #if RANGES_CXX_IF_CONSTEXPR < RANGES_CXX_IF_CONSTEXPR_17
  199. template<typename I>
  200. constexpr void advance_fn::n_(I & i, iter_difference_t<I> n,
  201. detail::input_iterator_tag_)
  202. {
  203. RANGES_EXPECT(n >= 0);
  204. for(; n > 0; --n)
  205. ++i;
  206. }
  207. template<typename I>
  208. constexpr void advance_fn::n_(I & i, iter_difference_t<I> n,
  209. detail::bidirectional_iterator_tag_)
  210. {
  211. if(n > 0)
  212. for(; n > 0; --n)
  213. ++i;
  214. else
  215. for(; n < 0; ++n)
  216. --i;
  217. }
  218. template<typename I>
  219. constexpr void advance_fn::n_(I & i, iter_difference_t<I> n,
  220. detail::random_access_iterator_tag_)
  221. {
  222. i += n;
  223. }
  224. template<typename I, typename S>
  225. constexpr void advance_fn::to_impl_(I & i, S s, sentinel_tag)
  226. {
  227. while(i != s)
  228. ++i;
  229. }
  230. template<typename I, typename S>
  231. constexpr void advance_fn::to_impl_(I & i, S s, sized_sentinel_tag)
  232. {
  233. iter_difference_t<I> d = s - i;
  234. RANGES_EXPECT(0 <= d);
  235. advance(i, d);
  236. }
  237. // Advance to a certain position:
  238. template<typename I, typename S>
  239. constexpr void advance_fn::to_(I & i, S s, std::true_type)
  240. {
  241. i = static_cast<S &&>(s);
  242. }
  243. template<typename I, typename S>
  244. constexpr void advance_fn::to_(I & i, S s, std::false_type)
  245. {
  246. advance_fn::to_impl_(i, static_cast<S &&>(s), sentinel_tag_of<S, I>());
  247. }
  248. template<typename I, typename S>
  249. constexpr iter_difference_t<I> advance_fn::bounded_(I & it, iter_difference_t<I> n,
  250. S bound, sentinel_tag,
  251. detail::input_iterator_tag_)
  252. {
  253. RANGES_EXPECT(0 <= n);
  254. for(; 0 != n && it != bound; --n)
  255. ++it;
  256. return n;
  257. }
  258. template<typename I>
  259. constexpr iter_difference_t<I> advance_fn::bounded_(
  260. I & it, iter_difference_t<I> n, I bound, sentinel_tag,
  261. detail::bidirectional_iterator_tag_)
  262. {
  263. if(0 <= n)
  264. for(; 0 != n && it != bound; --n)
  265. ++it;
  266. else
  267. for(; 0 != n && it != bound; ++n)
  268. --it;
  269. return n;
  270. }
  271. template<typename I, typename S, typename Concept>
  272. constexpr iter_difference_t<I> advance_fn::bounded_(I & it, iter_difference_t<I> n,
  273. S bound, sized_sentinel_tag,
  274. Concept)
  275. {
  276. RANGES_EXPECT(((bool)same_as<I, S> || 0 <= n));
  277. if(n == 0)
  278. return 0;
  279. iter_difference_t<I> d = bound - it;
  280. RANGES_EXPECT(0 <= n ? 0 <= d : 0 >= d);
  281. if(0 <= n ? n >= d : n <= d)
  282. {
  283. advance(it, static_cast<S &&>(bound));
  284. return n - d;
  285. }
  286. advance(it, n);
  287. return 0;
  288. }
  289. #endif
  290. struct next_fn
  291. {
  292. template<typename I>
  293. constexpr auto operator()(I it) const
  294. -> CPP_ret(I)(requires input_or_output_iterator<I>)
  295. {
  296. return ++it;
  297. }
  298. template<typename I>
  299. constexpr auto operator()(I it, iter_difference_t<I> n) const
  300. -> CPP_ret(I)(requires input_or_output_iterator<I>)
  301. {
  302. advance(it, n);
  303. return it;
  304. }
  305. template<typename I, typename S>
  306. constexpr auto operator()(I it, S s) const
  307. -> CPP_ret(I)(requires sentinel_for<S, I>)
  308. {
  309. advance(it, static_cast<S &&>(s));
  310. return it;
  311. }
  312. template<typename I, typename S>
  313. constexpr auto operator()(I it, iter_difference_t<I> n, S bound) const
  314. -> CPP_ret(I)(requires sentinel_for<S, I>)
  315. {
  316. advance(it, n, static_cast<S &&>(bound));
  317. return it;
  318. }
  319. };
  320. /// \sa `next_fn`
  321. RANGES_INLINE_VARIABLE(next_fn, next)
  322. struct prev_fn
  323. {
  324. template<typename I>
  325. constexpr auto operator()(I it) const
  326. -> CPP_ret(I)(requires bidirectional_iterator<I>)
  327. {
  328. return --it;
  329. }
  330. template<typename I>
  331. constexpr auto operator()(I it, iter_difference_t<I> n) const
  332. -> CPP_ret(I)(requires bidirectional_iterator<I>)
  333. {
  334. advance(it, -n);
  335. return it;
  336. }
  337. template<typename I>
  338. constexpr auto operator()(I it, iter_difference_t<I> n, I bound) const
  339. -> CPP_ret(I)(requires bidirectional_iterator<I>)
  340. {
  341. advance(it, -n, static_cast<I &&>(bound));
  342. return it;
  343. }
  344. };
  345. /// \sa `prev_fn`
  346. RANGES_INLINE_VARIABLE(prev_fn, prev)
  347. struct iter_enumerate_fn
  348. {
  349. private:
  350. template<typename I, typename S>
  351. static constexpr auto impl_i(I first, S last, sentinel_tag)
  352. -> CPP_ret(std::pair<iter_difference_t<I>, I>)( //
  353. requires(!sized_sentinel_for<I, I>))
  354. {
  355. iter_difference_t<I> d = 0;
  356. for(; first != last; ++first)
  357. ++d;
  358. return {d, first};
  359. }
  360. template<typename I, typename S>
  361. static constexpr auto impl_i(I first, S end_, sentinel_tag)
  362. -> CPP_ret(std::pair<iter_difference_t<I>, I>)( //
  363. requires sized_sentinel_for<I, I>)
  364. {
  365. I last = ranges::next(first, end_);
  366. auto n = static_cast<iter_difference_t<I>>(last - first);
  367. RANGES_EXPECT(((bool)same_as<I, S> || 0 <= n));
  368. return {n, last};
  369. }
  370. template<typename I, typename S>
  371. static constexpr std::pair<iter_difference_t<I>, I> impl_i(I first, S last,
  372. sized_sentinel_tag)
  373. {
  374. auto n = static_cast<iter_difference_t<I>>(last - first);
  375. RANGES_EXPECT(((bool)same_as<I, S> || 0 <= n));
  376. return {n, ranges::next(first, last)};
  377. }
  378. public:
  379. template<typename I, typename S>
  380. constexpr auto operator()(I first, S last) const
  381. -> CPP_ret(std::pair<iter_difference_t<I>, I>)( //
  382. requires sentinel_for<S, I>)
  383. {
  384. return iter_enumerate_fn::impl_i(static_cast<I &&>(first),
  385. static_cast<S &&>(last),
  386. sentinel_tag_of<S, I>());
  387. }
  388. };
  389. /// \sa `iter_enumerate_fn`
  390. RANGES_INLINE_VARIABLE(iter_enumerate_fn, iter_enumerate)
  391. struct iter_distance_fn
  392. {
  393. private:
  394. template<typename I, typename S>
  395. static constexpr iter_difference_t<I> impl_i(I first, S last, sentinel_tag)
  396. {
  397. return iter_enumerate(static_cast<I &&>(first), static_cast<S &&>(last)).first;
  398. }
  399. template<typename I, typename S>
  400. static constexpr iter_difference_t<I> impl_i(I first, S last, sized_sentinel_tag)
  401. {
  402. auto n = static_cast<iter_difference_t<I>>(last - first);
  403. RANGES_EXPECT(((bool)same_as<I, S> || 0 <= n));
  404. return n;
  405. }
  406. public:
  407. template<typename I, typename S>
  408. constexpr auto operator()(I first, S last) const
  409. -> CPP_ret(iter_difference_t<I>)( //
  410. requires input_or_output_iterator<I> && sentinel_for<S, I>)
  411. {
  412. return iter_distance_fn::impl_i(static_cast<I &&>(first),
  413. static_cast<S &&>(last),
  414. sentinel_tag_of<S, I>());
  415. }
  416. };
  417. /// \sa `iter_distance_fn`
  418. RANGES_INLINE_VARIABLE(iter_distance_fn, iter_distance)
  419. struct iter_distance_compare_fn
  420. {
  421. private:
  422. template<typename I, typename S>
  423. static constexpr int impl_i(I first, S last, iter_difference_t<I> n, sentinel_tag)
  424. {
  425. if(n < 0)
  426. return 1;
  427. for(; n > 0; --n, ++first)
  428. {
  429. if(first == last)
  430. return -1;
  431. }
  432. return first == last ? 0 : 1;
  433. }
  434. template<typename I, typename S>
  435. static constexpr int impl_i(I first, S last, iter_difference_t<I> n,
  436. sized_sentinel_tag)
  437. {
  438. iter_difference_t<I> dist = last - first;
  439. if(n < dist)
  440. return 1;
  441. if(dist < n)
  442. return -1;
  443. return 0;
  444. }
  445. public:
  446. template<typename I, typename S>
  447. constexpr auto operator()(I first, S last, iter_difference_t<I> n) const
  448. -> CPP_ret(int)( //
  449. requires input_iterator<I> && sentinel_for<S, I>)
  450. {
  451. return iter_distance_compare_fn::impl_i(static_cast<I &&>(first),
  452. static_cast<S &&>(last),
  453. n,
  454. sentinel_tag_of<S, I>());
  455. }
  456. };
  457. /// \sa `iter_distance_compare_fn`
  458. RANGES_INLINE_VARIABLE(iter_distance_compare_fn, iter_distance_compare)
  459. // Like distance(b,e), but guaranteed to be O(1)
  460. struct iter_size_fn
  461. {
  462. template<typename I, typename S>
  463. constexpr auto operator()(I const & first, S last) const
  464. -> CPP_ret(meta::_t<std::make_unsigned<iter_difference_t<I>>>)( //
  465. requires sized_sentinel_for<S, I>)
  466. {
  467. using size_type = meta::_t<std::make_unsigned<iter_difference_t<I>>>;
  468. iter_difference_t<I> n = last - first;
  469. RANGES_EXPECT(0 <= n);
  470. return static_cast<size_type>(n);
  471. }
  472. };
  473. /// \sa `iter_size_fn`
  474. RANGES_INLINE_VARIABLE(iter_size_fn, iter_size)
  475. /// \cond
  476. namespace adl_uncounted_recounted_detail
  477. {
  478. template<typename I>
  479. constexpr I uncounted(I i)
  480. {
  481. return i;
  482. }
  483. template<typename I>
  484. constexpr I recounted(I const &, I i, iter_difference_t<I>)
  485. {
  486. return i;
  487. }
  488. struct uncounted_fn
  489. {
  490. template<typename I>
  491. constexpr auto operator()(I i) const -> decltype(uncounted((I &&) i))
  492. {
  493. return uncounted((I &&) i);
  494. }
  495. };
  496. struct recounted_fn
  497. {
  498. template<typename I, typename J>
  499. constexpr auto operator()(I i, J j, iter_difference_t<J> n) const
  500. -> decltype(recounted((I &&) i, (J &&) j, n))
  501. {
  502. return recounted((I &&) i, (J &&) j, n);
  503. }
  504. };
  505. } // namespace adl_uncounted_recounted_detail
  506. /// \endcond
  507. RANGES_INLINE_VARIABLE(adl_uncounted_recounted_detail::uncounted_fn, uncounted)
  508. RANGES_INLINE_VARIABLE(adl_uncounted_recounted_detail::recounted_fn, recounted)
  509. struct enumerate_fn : iter_enumerate_fn
  510. {
  511. private:
  512. template<typename Rng>
  513. static std::pair<range_difference_t<Rng>, iterator_t<Rng>> impl_r(Rng & rng,
  514. range_tag,
  515. range_tag)
  516. {
  517. return iter_enumerate(begin(rng), end(rng));
  518. }
  519. template<typename Rng>
  520. static std::pair<range_difference_t<Rng>, iterator_t<Rng>> impl_r(
  521. Rng & rng, common_range_tag, sized_range_tag)
  522. {
  523. return {static_cast<range_difference_t<Rng>>(size(rng)), end(rng)};
  524. }
  525. public:
  526. using iter_enumerate_fn::operator();
  527. template<typename Rng>
  528. auto operator()(Rng && rng) const
  529. -> CPP_ret(std::pair<range_difference_t<Rng>, iterator_t<Rng>>)( //
  530. requires range<Rng>)
  531. {
  532. // Better not be trying to compute the distance of an infinite range:
  533. RANGES_EXPECT(!is_infinite<Rng>::value);
  534. return enumerate_fn::impl_r(
  535. rng, common_range_tag_of<Rng>(), sized_range_tag_of<Rng>());
  536. }
  537. };
  538. /// \sa `enumerate_fn`
  539. RANGES_INLINE_VARIABLE(enumerate_fn, enumerate)
  540. struct distance_fn : iter_distance_fn
  541. {
  542. private:
  543. template<typename Rng>
  544. static range_difference_t<Rng> impl_r(Rng & rng, range_tag)
  545. {
  546. return enumerate(rng).first;
  547. }
  548. template<typename Rng>
  549. constexpr static range_difference_t<Rng> impl_r(Rng & rng, sized_range_tag)
  550. {
  551. return static_cast<range_difference_t<Rng>>(size(rng));
  552. }
  553. public:
  554. using iter_distance_fn::operator();
  555. template<typename Rng>
  556. constexpr auto operator()(Rng && rng) const
  557. -> CPP_ret(range_difference_t<Rng>)( //
  558. requires range<Rng>)
  559. {
  560. // Better not be trying to compute the distance of an infinite range:
  561. RANGES_EXPECT(!is_infinite<Rng>::value);
  562. return distance_fn::impl_r(rng, sized_range_tag_of<Rng>());
  563. }
  564. };
  565. /// \sa `distance_fn`
  566. RANGES_INLINE_VARIABLE(distance_fn, distance)
  567. // The interface of distance_compare is taken from Util.listLengthCmp in the GHC API.
  568. struct distance_compare_fn : iter_distance_compare_fn
  569. {
  570. private:
  571. template<typename Rng>
  572. static int impl_r(Rng & rng, range_difference_t<Rng> n, range_tag)
  573. {
  574. // Infinite ranges are always compared to be larger than a finite number.
  575. return is_infinite<Rng>::value
  576. ? 1
  577. : iter_distance_compare(begin(rng), end(rng), n);
  578. }
  579. template<typename Rng>
  580. static int impl_r(Rng & rng, range_difference_t<Rng> n, sized_range_tag)
  581. {
  582. auto dist = distance(rng); // O(1) since rng is a sized_range
  583. if(dist > n)
  584. return 1;
  585. else if(dist < n)
  586. return -1;
  587. else
  588. return 0;
  589. }
  590. public:
  591. using iter_distance_compare_fn::operator();
  592. template<typename Rng>
  593. auto operator()(Rng && rng, range_difference_t<Rng> n) const -> CPP_ret(int)( //
  594. requires range<Rng>)
  595. {
  596. return distance_compare_fn::impl_r(rng, n, sized_range_tag_of<Rng>());
  597. }
  598. };
  599. /// \sa `distance_compare_fn`
  600. RANGES_INLINE_VARIABLE(distance_compare_fn, distance_compare)
  601. namespace cpp20
  602. {
  603. using ranges::advance;
  604. using ranges::distance;
  605. using ranges::next;
  606. using ranges::prev;
  607. } // namespace cpp20
  608. /// @}
  609. } // namespace ranges
  610. #endif // RANGES_V3_ITERATOR_OPERATIONS_HPP