From a883a5eb348d21c2d7d6561b37cee948f0a32f93 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Wed, 27 Nov 2024 20:24:33 +0100 Subject: [PATCH] P3138R5 views::cache_latest --- source/ranges.tex | 329 ++++++++++++++++++++++++++++++++++++++++++++- source/support.tex | 1 + 2 files changed, 329 insertions(+), 1 deletion(-) diff --git a/source/ranges.tex b/source/ranges.tex index 18fd5561d3..02b8936913 100644 --- a/source/ranges.tex +++ b/source/ranges.tex @@ -541,6 +541,13 @@ class cartesian_product_view; // freestanding namespace views { inline constexpr @\unspecnc@ cartesian_product = @\unspecnc@; } // freestanding + + // \ref{range.cache.latest}, cache latest view + template + requires view + class cache_latest_view; + + namepspace views { inline constexpr @\unspec@ cache_latest = @\unspec@; } } namespace std { @@ -15684,7 +15691,7 @@ \begin{itemdescr} \pnum \effects -Equivalent to: \tcode{++*this;} +Equivalent to \tcode{++*this;} \end{itemdescr} \indexlibrarymember{operator++}{stride_view::\exposid{iterator}}% @@ -16737,6 +16744,326 @@ \end{itemize} \end{itemdescr} +\rSec2[range.cache.latest]{Cache latest view} + +\rSec3[range.cache.latest.overview]{Overview} + +\pnum +\tcode{cache_latest_view} caches the last-accessed element of +its underlying sequence +so that the element does not have to be recomputed on repeated access. +\begin{note} +This is useful if computation of the element to produce is expensive. +\end{note} + +\pnum +The name \tcode{views::cache_latest} denotes +a range adaptor object\iref{range.adaptor.object}. +Let \tcode{E} be an expression. +The expression \tcode{views::cache_latest(E)} is expression-equivalent to +\tcode{cache_latest_view(E)}. + +\rSec3[range.cache.latest.view]{Class template \tcode{cache_latest_view}} + +\begin{codeblock} +namespace std::ranges { + template<@\libconcept{input_range}@ V> + requires @\libconcept{view}@ + class cache_latest_view : public view_interface> { + V @\exposid{base_}@ = V(); // \expos + using @\exposid{cache_t}@ = conditional_t>, // \expos + add_pointer_t>, + range_reference_t>; + + @\exposid{non-propagating-cache}@ @\exposid{cache_}@; // \expos + + class @\exposid{iterator}@; // \expos + class @\exposid{sentinel}@; // \expos + + public: + cache_latest_view() requires @\libconcept{default_initializable}@ = default; + constexpr explicit cache_latest_view(V base); + + constexpr V base() const & requires @\libconcept{copy_constructible}@ { return @\exposid{base_}@; } + constexpr V base() && { return std::move(@\exposid{base_}@); } + + constexpr auto begin(); + constexpr auto end(); + + constexpr auto size() requires @\libconcept{sized_range}@; + constexpr auto size() const requires @\libconcept{sized_range}@; + }; + + template + cache_latest_view(R&&) -> cache_latest_view>; +} +\end{codeblock} + +\begin{itemdecl} +constexpr explicit cache_latest_view(V base); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{base_} with \tcode{std::move(base)}. +\end{itemdescr} + +\begin{itemdecl} +constexpr auto begin(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return \exposid{iterator}(*this);} +\end{itemdescr} + +\begin{itemdecl} +constexpr auto end(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return \exposid{sentinel}(*this);} +\end{itemdescr} + +\begin{itemdecl} +constexpr auto size() requires sized_range; +constexpr auto size() const requires sized_range; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return ranges::size(\exposid{base_});} +\end{itemdescr} + +\rSec3[range.cache.latest.iterator]{Class \tcode{cache_latest_view::\exposid{iterator}}} + +\begin{codeblock} +namespace std::ranges { + template<@\libconcept{input_range}@ V> + requires @\libconcept{view}@ + class cache_latest_view::@\exposid{iterator}@ { + cache_latest_view* @\exposid{parent_}@; // \expos + iterator_t @\exposid{current_}@; // \expos + + constexpr explicit @\exposid{iterator}@(cache_latest_view& parent); // \expos + + public: + using difference_type = range_difference_t; + using value_type = range_value_t; + using iterator_concept = input_iterator_tag; + + @\exposid{iterator}@(@\exposid{iterator}@&&) = default; + @\exposid{iterator}@& operator=(@\exposid{iterator}@&&) = default; + + constexpr iterator_t base() &&; + constexpr const iterator_t& base() const & noexcept; + + constexpr range_reference_t& operator*() const; + + constexpr @\exposid{iterator}@& operator++(); + constexpr void operator++(int); + + friend constexpr range_rvalue_reference_t iter_move(const @\exposid{iterator}@& i) + noexcept(noexcept(ranges::iter_move(i.@\exposid{current_}@))); + + friend constexpr void iter_swap(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + noexcept(noexcept(ranges::iter_swap(x.@\exposid{current_}@, y.@\exposid{current_}@))) + requires @\libconcept{indirectly_swappable}@>; + }; +} +\end{codeblock} + +\begin{itemdecl} +constexpr explicit @\exposid{iterator}@(cache_latest_view& parent); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{current_} with +\tcode{ranges::begin(parent.\exposid{base_})} +and \exposid{parent_} with \tcode{addressof(parent)}. +\end{itemdescr} + +\begin{itemdecl} +constexpr iterator_t base() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(\exposid{current_})}. +\end{itemdescr} + +\begin{itemdecl} +constexpr const iterator_t& base() const & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{current_}. +\end{itemdescr} + +\begin{itemdecl} +constexpr iterator& operator++(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +@\exposid{parent_}@->@\exposid{cache_}@.reset(); +++@\exposid{current_}@; +return *this; +\end{codeblock} +\end{itemdescr} + +\begin{itemdecl} +constexpr void operator++(int); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{++*this}. +\end{itemdescr} + +\begin{itemdecl} +constexpr range_reference_t& operator*() const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +if constexpr (is_reference_v>) { + if (!@\exposid{parent_}@->@\exposid{cache_}@) { + @\exposid{parent_}@->@\exposid{cache_}@ = addressof(@\exposid{as-lvalue}@(*@\exposid{current_}@)); + } + return **@\exposid{parent_}@->@\exposid{cache_}@; +} else { + if (!@\exposid{parent_}@->c@\exposid{ache_}@) { + @\exposid{parent_}@->@\exposid{cache_}@.@\exposid{emplace-deref}@(@\exposid{current_}@); + } + return *@\exposid{parent_}@->@\exposid{cache_}@; +} +\end{codeblock} +\begin{note} +Evaluations of \tcode{operator*} on the same iterator object +can conflict\iref{intro.races}. +\end{note} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr range_rvalue_reference_t iter_move(const @\exposid{iterator}@& i) + noexcept(noexcept(ranges::iter_move(i.@\exposid{current_}@))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return ranges::iter_move(i.\exposid{current_});} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr void iter_swap(const @\exposid{iterator}@& x, const @\exposid{iterator}@& y) + noexcept(noexcept(ranges::iter_swap(x.@\exposid{current_}@, y.@\exposid{current_}@))) + requires @\libconcept{indirectly_swappable}@>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to +\tcode{ranges::iter_swap(x.\exposid{current_}, y.\exposid{current_})}. +\end{itemdescr} + +\rSec3[range.cache.latest.sentinel]{Class \tcode{cache_latest_view::\exposid{sentinel}}} + +\begin{codeblock} +namespace std::ranges { + template<@\libconcept{input_range}@ V> + requires @\libconcept{view}@ + class cache_latest_view::@\exposid{sentinel}@ { + sentinel_t @\exposid{end_}@ = sentinel_t(); // \expos + + constexpr explicit @\exposid{sentinel}@(cache_latest_view& parent); // \expos + + public: + @\exposid{sentinel}@() = default; + + constexpr sentinel_t base() const; + + friend constexpr bool operator==(const @\exposid{iterator}@& x, const @\exposid{sentinel}@& y); + + friend constexpr range_difference_t operator-(const @\exposid{iterator}@& x, const @\exposid{sentinel}@& y) + requires sized_sentinel_for, iterator_t>; + friend constexpr range_difference_t operator-(const @\exposid{sentinel}@& x, const @\exposid{iterator}@& y) + requires sized_sentinel_for, iterator_t>; + }; +} +\end{codeblock} + +\begin{itemdecl} +constexpr explicit @\exposid{sentinel}@(cache_latest_view& parent); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{end_} with \tcode{ranges::end(parent.\exposid{base_})}. +\end{itemdescr} + +\begin{itemdecl} +constexpr sentinel_t base() const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{end_}. +\end{itemdescr} + +\begin{itemdecl} +friend constexpr bool operator==(const iterator& x, const sentinel& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{x.\exposid{current_} == y.\exposid{end_}}. +\end{itemdescr} + +\begin{itemdecl} +friend constexpr range_difference_t operator-(const @\exposid{iterator}@& x, const @\exposid{sentinel}@& y) + requires sized_sentinel_for, iterator_t>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{x.\exposid{current_} - y.\exposid{end_}}. +\end{itemdescr} + +\begin{itemdecl} +friend constexpr range_difference_t operator-(const @\exposid{sentinel}@& x, const @\exposid{iterator}@& y) + requires sized_sentinel_for, iterator_t>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{x.\exposid{end_} - y.\exposid{current_}}. +\end{itemdescr} + \rSec1[coro.generator]{Range generators} \rSec2[coroutine.generator.overview]{Overview} diff --git a/source/support.tex b/source/support.tex index 28c15ac913..b72181ab30 100644 --- a/source/support.tex +++ b/source/support.tex @@ -739,6 +739,7 @@ // also in \libheader{algorithm}, \libheader{functional}, \libheader{iterator}, \libheader{memory}, \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_as_const}@ 202311L // freestanding, also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_as_rvalue}@ 202207L // freestanding, also in \libheader{ranges} +#define @\defnlibxname{cpp_lib_ranges_cache_latest}@ 202411L // also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_cartesian_product}@ 202207L // freestanding, also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_chunk}@ 202202L // freestanding, also in \libheader{ranges} #define @\defnlibxname{cpp_lib_ranges_chunk_by}@ 202202L // freestanding, also in \libheader{ranges}