join.hpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  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_VIEW_JOIN_HPP
  14. #define RANGES_V3_VIEW_JOIN_HPP
  15. #include <type_traits>
  16. #include <utility>
  17. #include <meta/meta.hpp>
  18. #include <range/v3/range_fwd.hpp>
  19. #include <range/v3/functional/bind_back.hpp>
  20. #include <range/v3/iterator/default_sentinel.hpp>
  21. #include <range/v3/range/access.hpp>
  22. #include <range/v3/range/primitives.hpp>
  23. #include <range/v3/range/traits.hpp>
  24. #include <range/v3/range_for.hpp>
  25. #include <range/v3/utility/static_const.hpp>
  26. #include <range/v3/utility/variant.hpp>
  27. #include <range/v3/view/all.hpp>
  28. #include <range/v3/view/facade.hpp>
  29. #include <range/v3/view/single.hpp>
  30. #include <range/v3/view/view.hpp>
  31. #include <range/v3/detail/prologue.hpp>
  32. namespace ranges
  33. {
  34. /// \cond
  35. namespace detail
  36. {
  37. // Compute the cardinality of a joined range
  38. constexpr cardinality join_cardinality_(
  39. cardinality Outer, cardinality Inner,
  40. cardinality Joiner = static_cast<cardinality>(0)) noexcept
  41. {
  42. return Outer == infinite || Inner == infinite ||
  43. (Joiner == infinite && Outer != 0 && Outer != 1)
  44. ? infinite
  45. : Outer == unknown || Inner == unknown ||
  46. (Joiner == unknown && Outer != 0 && Outer != 1)
  47. ? unknown
  48. : Outer == finite || Inner == finite ||
  49. (Joiner == finite && Outer != 0 && Outer != 1)
  50. ? finite
  51. : static_cast<cardinality>(
  52. Outer * Inner +
  53. (Outer == 0 ? 0 : (Outer - 1) * Joiner));
  54. }
  55. template<typename Range>
  56. constexpr cardinality join_cardinality() noexcept
  57. {
  58. return detail::join_cardinality_(
  59. range_cardinality<Range>::value,
  60. range_cardinality<range_reference_t<Range>>::value);
  61. }
  62. template<typename Range, typename JoinRange>
  63. constexpr cardinality join_cardinality() noexcept
  64. {
  65. return detail::join_cardinality_(
  66. range_cardinality<Range>::value,
  67. range_cardinality<range_reference_t<Range>>::value,
  68. range_cardinality<JoinRange>::value);
  69. }
  70. template<typename Inner>
  71. struct store_inner_
  72. {
  73. non_propagating_cache<std::remove_cv_t<Inner>> inner_ = {};
  74. template<typename OuterIt>
  75. constexpr auto && update_inner_(OuterIt && it)
  76. {
  77. return inner_.emplace_deref(it);
  78. }
  79. constexpr Inner & get_inner_(ignore_t) noexcept
  80. {
  81. return *inner_;
  82. }
  83. };
  84. struct pass_thru_inner_
  85. {
  86. // Intentionally promote xvalues to lvalues here:
  87. template<typename OuterIt>
  88. static constexpr auto && update_inner_(OuterIt && it) noexcept
  89. {
  90. return *it;
  91. }
  92. template<typename OuterIt>
  93. static constexpr decltype(auto) get_inner_(OuterIt && outer_it)
  94. {
  95. return *outer_it;
  96. }
  97. };
  98. template<typename Rng>
  99. using join_view_inner =
  100. meta::conditional_t<!std::is_reference<range_reference_t<Rng>>::value,
  101. store_inner_<range_reference_t<Rng>>, pass_thru_inner_>;
  102. // clang-format off
  103. /// \concept has_member_arrow_
  104. /// \brief The \c has_member_arrow_ concept
  105. template<typename I>
  106. CPP_requires(has_member_arrow_,
  107. requires(I i) //
  108. (
  109. i.operator->()
  110. ));
  111. /// \concept has_arrow_
  112. /// \brief The \c has_arrow_ concept
  113. template<typename I>
  114. CPP_concept has_arrow_ =
  115. input_iterator<I> &&
  116. (std::is_pointer<I>::value || CPP_requires_ref(detail::has_member_arrow_, I));
  117. // clang-format on
  118. } // namespace detail
  119. /// \endcond
  120. /// \addtogroup group-views
  121. /// @{
  122. // Join a range of ranges
  123. template<typename Rng>
  124. struct RANGES_EMPTY_BASES join_view
  125. : view_facade<join_view<Rng>, detail::join_cardinality<Rng>()>
  126. , private detail::join_view_inner<Rng>
  127. {
  128. CPP_assert(input_range<Rng> && view_<Rng>);
  129. CPP_assert(input_range<range_reference_t<Rng>>);
  130. join_view() = default;
  131. explicit join_view(Rng rng)
  132. : outer_(views::all(std::move(rng)))
  133. {}
  134. // Not to spec
  135. CPP_member
  136. static constexpr auto size() //
  137. -> CPP_ret(std::size_t)(
  138. requires (detail::join_cardinality<Rng>() >= 0))
  139. {
  140. return static_cast<std::size_t>(detail::join_cardinality<Rng>());
  141. }
  142. // Not to spec
  143. CPP_auto_member
  144. constexpr auto CPP_fun(size)()(
  145. requires(detail::join_cardinality<Rng>() < 0) &&
  146. (range_cardinality<Rng>::value >= 0) &&
  147. forward_range<Rng> &&
  148. sized_range<range_reference_t<Rng>>)
  149. {
  150. range_size_t<range_reference_t<Rng>> n = 0;
  151. RANGES_FOR(auto && inner, outer_)
  152. n += ranges::size(inner);
  153. return n;
  154. }
  155. // // ericniebler/stl2#605
  156. constexpr Rng base() const
  157. {
  158. return outer_;
  159. }
  160. private:
  161. friend range_access;
  162. Rng outer_{};
  163. template<bool Const>
  164. struct cursor
  165. {
  166. private:
  167. using Parent = meta::conditional_t<Const, join_view const, join_view>;
  168. using COuter = meta::conditional_t<Const, Rng const, Rng>;
  169. using CInner = range_reference_t<COuter>;
  170. using ref_is_glvalue = std::is_reference<CInner>;
  171. Parent * rng_ = nullptr;
  172. iterator_t<COuter> outer_it_{};
  173. iterator_t<CInner> inner_it_{};
  174. void satisfy()
  175. {
  176. for(; outer_it_ != ranges::end(rng_->outer_); ++outer_it_)
  177. {
  178. auto && inner = rng_->update_inner_(outer_it_);
  179. inner_it_ = ranges::begin(inner);
  180. if(inner_it_ != ranges::end(inner))
  181. return;
  182. }
  183. if(RANGES_CONSTEXPR_IF(ref_is_glvalue::value))
  184. inner_it_ = iterator_t<CInner>();
  185. }
  186. public:
  187. using single_pass = meta::bool_<single_pass_iterator_<iterator_t<COuter>> ||
  188. single_pass_iterator_<iterator_t<CInner>> ||
  189. !ref_is_glvalue::value>;
  190. cursor() = default;
  191. template<typename BeginOrEnd>
  192. constexpr cursor(Parent * rng, BeginOrEnd begin_or_end)
  193. : rng_{rng}
  194. , outer_it_(begin_or_end(rng->outer_))
  195. {
  196. satisfy();
  197. }
  198. template(bool Other)(
  199. requires Const AND CPP_NOT(Other) AND
  200. convertible_to<iterator_t<Rng>, iterator_t<COuter>> AND
  201. convertible_to<iterator_t<range_reference_t<Rng>>,
  202. iterator_t<CInner>>)
  203. constexpr cursor(cursor<Other> that)
  204. : rng_(that.rng_)
  205. , outer_it_(std::move(that.outer_it_))
  206. , inner_it_(std::move(that.inner_it_))
  207. {}
  208. CPP_member
  209. constexpr auto arrow() //
  210. -> CPP_ret(iterator_t<CInner>)(
  211. requires detail::has_arrow_<iterator_t<CInner>>)
  212. {
  213. return inner_it_;
  214. }
  215. constexpr bool equal(default_sentinel_t) const
  216. {
  217. return outer_it_ == ranges::end(rng_->outer_);
  218. }
  219. CPP_member
  220. constexpr auto equal(cursor const & that) const //
  221. -> CPP_ret(bool)(
  222. requires ref_is_glvalue::value && //
  223. equality_comparable<iterator_t<COuter>> && //
  224. equality_comparable<iterator_t<CInner>>)
  225. {
  226. return outer_it_ == that.outer_it_ && inner_it_ == that.inner_it_;
  227. }
  228. constexpr void next()
  229. {
  230. auto && inner_rng = rng_->get_inner_(outer_it_);
  231. if(++inner_it_ == ranges::end(inner_rng))
  232. {
  233. ++outer_it_;
  234. satisfy();
  235. }
  236. }
  237. CPP_member
  238. constexpr auto prev() //
  239. -> CPP_ret(void)(
  240. requires ref_is_glvalue::value && //
  241. bidirectional_range<COuter> && //
  242. bidirectional_range<CInner> && //
  243. common_range<CInner>) // ericniebler/stl2#606
  244. {
  245. if(outer_it_ == ranges::end(rng_->outer_))
  246. inner_it_ = ranges::end(*--outer_it_);
  247. while(inner_it_ == ranges::begin(*outer_it_))
  248. inner_it_ = ranges::end(*--outer_it_);
  249. --inner_it_;
  250. }
  251. // clang-format off
  252. constexpr auto CPP_auto_fun(read)()(const)
  253. (
  254. return *inner_it_
  255. )
  256. constexpr auto CPP_auto_fun(move)()(const)
  257. (
  258. return iter_move(inner_it_)
  259. )
  260. // clang-format on
  261. };
  262. static constexpr bool use_const_always() noexcept
  263. {
  264. return simple_view<Rng>() && std::is_reference<range_reference_t<Rng>>::value;
  265. }
  266. struct end_cursor_fn
  267. {
  268. constexpr auto operator()(join_view * this_, std::true_type) const
  269. {
  270. return cursor<use_const_always()>{this_, ranges::end};
  271. }
  272. constexpr auto operator()(join_view *, std::false_type) const
  273. {
  274. return default_sentinel_t{};
  275. }
  276. };
  277. struct cend_cursor_fn
  278. {
  279. constexpr auto operator()(join_view const * this_, std::true_type) const
  280. {
  281. return cursor<true>{this_, ranges::end};
  282. }
  283. constexpr auto operator()(join_view const *, std::false_type) const
  284. {
  285. return default_sentinel_t{};
  286. }
  287. };
  288. constexpr cursor<use_const_always()> begin_cursor()
  289. {
  290. return {this, ranges::begin};
  291. }
  292. template(bool Const = true)(
  293. requires Const AND input_range<meta::const_if_c<Const, Rng>> AND
  294. std::is_reference<range_reference_t<meta::const_if_c<Const, Rng>>>::value)
  295. constexpr cursor<Const> begin_cursor() const
  296. {
  297. return {this, ranges::begin};
  298. }
  299. constexpr auto end_cursor()
  300. {
  301. using cond =
  302. meta::bool_<std::is_reference<range_reference_t<Rng>>::value &&
  303. forward_range<Rng> && forward_range<range_reference_t<Rng>> &&
  304. common_range<Rng> && common_range<range_reference_t<Rng>>>;
  305. return end_cursor_fn{}(this, cond{});
  306. }
  307. template(bool Const = true)(
  308. requires Const AND input_range<meta::const_if_c<Const, Rng>> AND
  309. std::is_reference<range_reference_t<meta::const_if_c<Const, Rng>>>::value)
  310. constexpr auto end_cursor() const
  311. {
  312. using CRng = meta::const_if_c<Const, Rng>;
  313. using cond =
  314. meta::bool_<std::is_reference<range_reference_t<CRng>>::value &&
  315. forward_range<CRng> &&
  316. forward_range<range_reference_t<CRng>> &&
  317. common_range<CRng> && common_range<range_reference_t<CRng>>>;
  318. return cend_cursor_fn{}(this, cond{});
  319. }
  320. };
  321. // Join a range of ranges, inserting a range of values between them.
  322. // TODO: Support const iteration when range_reference_t<Rng> is a true reference.
  323. template<typename Rng, typename ValRng>
  324. struct join_with_view
  325. : view_facade<join_with_view<Rng, ValRng>, detail::join_cardinality<Rng, ValRng>()>
  326. , private detail::join_view_inner<Rng>
  327. {
  328. CPP_assert(input_range<Rng>);
  329. CPP_assert(input_range<range_reference_t<Rng>>);
  330. CPP_assert(forward_range<ValRng>);
  331. CPP_assert(
  332. common_with<range_value_t<range_reference_t<Rng>>, range_value_t<ValRng>>);
  333. CPP_assert(semiregular<common_type_t<range_value_t<range_reference_t<Rng>>,
  334. range_value_t<ValRng>>>);
  335. join_with_view() = default;
  336. join_with_view(Rng rng, ValRng val)
  337. : outer_(views::all(std::move(rng)))
  338. , val_(views::all(std::move(val)))
  339. {}
  340. CPP_member
  341. static constexpr auto size() //
  342. -> CPP_ret(std::size_t)(
  343. requires (detail::join_cardinality<Rng, ValRng>() >= 0))
  344. {
  345. return static_cast<std::size_t>(detail::join_cardinality<Rng, ValRng>());
  346. }
  347. CPP_auto_member
  348. auto CPP_fun(size)()(const //
  349. requires(detail::join_cardinality<Rng, ValRng>() < 0) &&
  350. (range_cardinality<Rng>::value >= 0) && forward_range<Rng> &&
  351. sized_range<range_reference_t<Rng>> && sized_range<ValRng>)
  352. {
  353. range_size_t<range_reference_t<Rng>> n = 0;
  354. RANGES_FOR(auto && inner, outer_)
  355. n += ranges::size(inner);
  356. return n + (range_cardinality<Rng>::value == 0
  357. ? 0
  358. : ranges::size(val_) * (range_cardinality<Rng>::value - 1));
  359. }
  360. private:
  361. friend range_access;
  362. using Outer = views::all_t<Rng>;
  363. // Intentionally promote xvalues to lvalues here:
  364. using Inner = views::all_t<range_reference_t<Outer> &>;
  365. Outer outer_{};
  366. views::all_t<ValRng> val_{};
  367. class cursor
  368. {
  369. join_with_view * rng_ = nullptr;
  370. iterator_t<Outer> outer_it_{};
  371. variant<iterator_t<ValRng>, iterator_t<Inner>> cur_{};
  372. void satisfy()
  373. {
  374. while(true)
  375. {
  376. if(cur_.index() == 0)
  377. {
  378. if(ranges::get<0>(cur_) != ranges::end(rng_->val_))
  379. break;
  380. // Intentionally promote xvalues to lvalues here:
  381. auto && inner = rng_->update_inner_(outer_it_);
  382. ranges::emplace<1>(cur_, ranges::begin(inner));
  383. }
  384. else
  385. {
  386. auto && inner = rng_->get_inner_(outer_it_);
  387. if(ranges::get<1>(cur_) != ranges::end(inner))
  388. break;
  389. if(++outer_it_ == ranges::end(rng_->outer_))
  390. break;
  391. ranges::emplace<0>(cur_, ranges::begin(rng_->val_));
  392. }
  393. }
  394. }
  395. public:
  396. using value_type = common_type_t<range_value_t<Inner>, range_value_t<ValRng>>;
  397. using reference =
  398. common_reference_t<range_reference_t<Inner>, range_reference_t<ValRng>>;
  399. using rvalue_reference = common_reference_t<range_rvalue_reference_t<Inner>,
  400. range_rvalue_reference_t<ValRng>>;
  401. using single_pass = std::true_type;
  402. cursor() = default;
  403. cursor(join_with_view * rng)
  404. : rng_{rng}
  405. , outer_it_(ranges::begin(rng->outer_))
  406. {
  407. if(outer_it_ != ranges::end(rng->outer_))
  408. {
  409. auto && inner = rng_->update_inner_(outer_it_);
  410. ranges::emplace<1>(cur_, ranges::begin(inner));
  411. satisfy();
  412. }
  413. }
  414. bool equal(default_sentinel_t) const
  415. {
  416. return outer_it_ == ranges::end(rng_->outer_);
  417. }
  418. void next()
  419. {
  420. // visit(cur_, [](auto& it){ ++it; });
  421. if(cur_.index() == 0)
  422. {
  423. auto & it = ranges::get<0>(cur_);
  424. RANGES_ASSERT(it != ranges::end(rng_->val_));
  425. ++it;
  426. }
  427. else
  428. {
  429. auto & it = ranges::get<1>(cur_);
  430. #ifndef NDEBUG
  431. auto && inner = rng_->get_inner_(outer_it_);
  432. RANGES_ASSERT(it != ranges::end(inner));
  433. #endif
  434. ++it;
  435. }
  436. satisfy();
  437. }
  438. reference read() const
  439. {
  440. // return visit(cur_, [](auto& it) -> reference { return *it; });
  441. if(cur_.index() == 0)
  442. return *ranges::get<0>(cur_);
  443. else
  444. return *ranges::get<1>(cur_);
  445. }
  446. rvalue_reference move() const
  447. {
  448. // return visit(cur_, [](auto& it) -> rvalue_reference { return
  449. // iter_move(it); });
  450. if(cur_.index() == 0)
  451. return iter_move(ranges::get<0>(cur_));
  452. else
  453. return iter_move(ranges::get<1>(cur_));
  454. }
  455. };
  456. cursor begin_cursor()
  457. {
  458. return {this};
  459. }
  460. };
  461. namespace views
  462. {
  463. /// \cond
  464. // Don't forget to update views::for_each whenever this set
  465. // of concepts changes
  466. // clang-format off
  467. /// \concept joinable_range_
  468. /// \brief The \c joinable_range_ concept
  469. template(typename Rng)(
  470. concept (joinable_range_)(Rng),
  471. input_range<range_reference_t<Rng>>
  472. );
  473. /// \concept joinable_range
  474. /// \brief The \c joinable_range concept
  475. template<typename Rng>
  476. CPP_concept joinable_range =
  477. viewable_range<Rng> && input_range<Rng> &&
  478. CPP_concept_ref(views::joinable_range_, Rng);
  479. /// \concept joinable_with_range_
  480. /// \brief The \c joinable_with_range_ concept
  481. template(typename Rng, typename ValRng)(
  482. concept (joinable_with_range_)(Rng, ValRng),
  483. common_with<
  484. range_value_t<ValRng>,
  485. range_value_t<range_reference_t<Rng>>> AND
  486. semiregular<
  487. common_type_t<
  488. range_value_t<ValRng>,
  489. range_value_t<range_reference_t<Rng>>>> AND
  490. common_reference_with<
  491. range_reference_t<ValRng>,
  492. range_reference_t<range_reference_t<Rng>>> AND
  493. common_reference_with<
  494. range_rvalue_reference_t<ValRng>,
  495. range_rvalue_reference_t<range_reference_t<Rng>>>
  496. );
  497. /// \concept joinable_with_range
  498. /// \brief The \c joinable_with_range concept
  499. template<typename Rng, typename ValRng>
  500. CPP_concept joinable_with_range =
  501. joinable_range<Rng> &&
  502. viewable_range<ValRng> && forward_range<ValRng> &&
  503. CPP_concept_ref(views::joinable_with_range_, Rng, ValRng);
  504. // clang-format on
  505. /// \endcond
  506. struct cpp20_join_fn
  507. {
  508. template(typename Rng)(
  509. requires joinable_range<Rng>)
  510. join_view<all_t<Rng>> operator()(Rng && rng) const
  511. {
  512. return join_view<all_t<Rng>>{all(static_cast<Rng &&>(rng))};
  513. }
  514. };
  515. struct join_base_fn : cpp20_join_fn
  516. {
  517. private:
  518. template<typename Rng>
  519. using inner_value_t = range_value_t<range_reference_t<Rng>>;
  520. public:
  521. using cpp20_join_fn::operator();
  522. template(typename Rng)(
  523. requires joinable_with_range<Rng, single_view<inner_value_t<Rng>>>)
  524. join_with_view<all_t<Rng>, single_view<inner_value_t<Rng>>> //
  525. operator()(Rng && rng, inner_value_t<Rng> v) const
  526. {
  527. return {all(static_cast<Rng &&>(rng)), single(std::move(v))};
  528. }
  529. template(typename Rng, typename ValRng)(
  530. requires joinable_with_range<Rng, ValRng>)
  531. join_with_view<all_t<Rng>, all_t<ValRng>> //
  532. operator()(Rng && rng, ValRng && val) const
  533. {
  534. return {all(static_cast<Rng &&>(rng)), all(static_cast<ValRng &&>(val))};
  535. }
  536. /// \cond
  537. template<typename Rng, typename T>
  538. invoke_result_t<join_base_fn, Rng, T &> //
  539. operator()(Rng && rng, detail::reference_wrapper_<T> r) const
  540. {
  541. return (*this)(static_cast<Rng &&>(rng), r.get());
  542. }
  543. /// \endcond
  544. };
  545. struct join_bind_fn
  546. {
  547. template(typename T)(
  548. requires (!joinable_range<T>)) // TODO: underconstrained
  549. constexpr auto operator()(T && t)const
  550. {
  551. return make_view_closure(bind_back(join_base_fn{}, static_cast<T &&>(t)));
  552. }
  553. template(typename T)(
  554. requires (!joinable_range<T &>) AND range<T &>)
  555. constexpr auto operator()(T & t) const
  556. {
  557. return make_view_closure(bind_back(join_base_fn{},
  558. detail::reference_wrapper_<T>(t)));
  559. }
  560. };
  561. struct RANGES_EMPTY_BASES join_fn
  562. : join_base_fn, join_bind_fn
  563. {
  564. using join_base_fn::operator();
  565. using join_bind_fn::operator();
  566. };
  567. /// \relates join_fn
  568. /// \ingroup group-views
  569. RANGES_INLINE_VARIABLE(view_closure<join_fn>, join)
  570. } // namespace views
  571. /// @}
  572. #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
  573. template(typename Rng)(
  574. requires views::joinable_range<Rng>)
  575. explicit join_view(Rng &&)
  576. ->join_view<views::all_t<Rng>>;
  577. template(typename Rng, typename ValRng)(
  578. requires views::joinable_with_range<Rng, ValRng>)
  579. explicit join_with_view(Rng &&, ValRng &&)
  580. ->join_with_view<views::all_t<Rng>, views::all_t<ValRng>>;
  581. #endif
  582. namespace cpp20
  583. {
  584. namespace views
  585. {
  586. RANGES_INLINE_VARIABLE(
  587. ranges::views::view_closure<ranges::views::cpp20_join_fn>, join)
  588. }
  589. template(typename Rng)(
  590. requires input_range<Rng> AND view_<Rng> AND
  591. input_range<iter_reference_t<iterator_t<Rng>>>) //
  592. using join_view = ranges::join_view<Rng>;
  593. } // namespace cpp20
  594. } // namespace ranges
  595. #include <range/v3/detail/epilogue.hpp>
  596. #include <range/v3/detail/satisfy_boost_range.hpp>
  597. RANGES_SATISFY_BOOST_RANGE(::ranges::join_view)
  598. RANGES_SATISFY_BOOST_RANGE(::ranges::join_with_view)
  599. #endif