diff --git a/papers/nxxxx.md b/papers/nxxxx.md index 9f7154fec9..cdcb91d6c9 100644 --- a/papers/nxxxx.md +++ b/papers/nxxxx.md @@ -63,6 +63,20 @@ and after consulting the paper authors and the LWG chair, the corresponding changes were also applied to the additional range adaptors listed above. +### LWG motion 16 + +This paper removed the exposition-only concept *`range-impl`*, +inlining it into its only remaining user, the `range` concept. +However, two uses of *`range-impl`* were left behind. +These have been updated and suitably adjusted +to refer to `range` instead. + +LWG motion 13 ([P1394R4](http://wg21.link/p1394r4)) +added a couple of new uses of +the exposition-only concept *`forwarding-range`*, +which was removed by this paper. +These uses have been replaced with `safe_range`. + ## Feature test macros The feature test macro `__cpp_nontype_template_parameter_class` has been removed diff --git a/source/containers.tex b/source/containers.tex index c2aa20e948..34f2fdc10d 100644 --- a/source/containers.tex +++ b/source/containers.tex @@ -10513,6 +10513,9 @@ template class span; + template + inline constexpr bool enable_safe_range> = true; + // \ref{span.objectrep}, views of object representation template span @@ -10625,9 +10628,6 @@ constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; - friend constexpr iterator begin(span s) noexcept { return s.begin(); } - friend constexpr iterator end(span s) noexcept { return s.end(); } - private: pointer data_; // \expos size_type size_; // \expos @@ -10786,7 +10786,7 @@ \item \tcode{extent == dynamic_extent} is \tcode{true}. \item \tcode{R} satisfies \tcode{ranges::\libconcept{contiguous_range}} and \tcode{ranges::\libconcept{sized_range}}. -\item Either \tcode{R} satisfies \exposconcept{forwarding-range} or +\item Either \tcode{R} satisfies \libconcept{safe_range} or \tcode{is_const_v} is \tcode{true}. \item \tcode{remove_cvref_t} is not a specialization of \tcode{span}. \item \tcode{remove_cvref_t} is not a specialization of \tcode{array}. @@ -10805,7 +10805,7 @@ \item \tcode{R} models \tcode{ranges::\libconcept{contiguous_range}} and \tcode{ranges::\libconcept{sized_range}}. \item If \tcode{is_const_v} is \tcode{false}, -\tcode{R} models \exposconcept{forwarding-range}. +\tcode{R} models \libconcept{safe_range}. \end{itemize} \pnum diff --git a/source/ranges.tex b/source/ranges.tex index b1b98ad91b..8fb4258db0 100644 --- a/source/ranges.tex +++ b/source/ranges.tex @@ -50,6 +50,12 @@ template concept range = @\seebelow@; + template + inline constexpr bool enable_safe_range = false; + + template + concept safe_range = @\seebelow@; + template using iterator_t = decltype(ranges::begin(declval())); template @@ -116,21 +122,27 @@ requires (K == subrange_kind::sized || !sized_sentinel_for) class subrange; + template S, subrange_kind K> + inline constexpr bool enable_safe_range> = true; + // \ref{range.dangling}, dangling iterator handling struct dangling; template - using safe_iterator_t = conditional_t<@\placeholder{forwarding-range}@, iterator_t, dangling>; + using safe_iterator_t = conditional_t, iterator_t, dangling>; template using safe_subrange_t = - conditional_t<@\placeholder{forwarding-range}@, subrange>, dangling>; + conditional_t, subrange>, dangling>; // \ref{range.empty}, empty view template requires is_object_v class empty_view; + template + inline constexpr bool enable_safe_range> = true; + namespace views { template inline constexpr empty_view empty{}; @@ -148,6 +160,9 @@ requires @\placeholder{weakly-equality-comparable-with}@ class iota_view; + template + inline constexpr bool enable_safe_range> = true; + namespace views { inline constexpr @\unspec@ iota = @\unspec@; } // \ref{range.all}, all view @@ -160,6 +175,9 @@ requires is_object_v class ref_view; + template + inline constexpr bool enable_safe_range> = true; + // \ref{range.filter}, filter view template> Pred> requires view && is_object_v @@ -306,21 +324,27 @@ \rSec2[range.access.begin]{\tcode{ranges::begin}} \pnum The name \tcode{ranges::begin} denotes a customization point -object\iref{customization.point.object}. The expression -\tcode{ranges::\brk{}begin(E)} for some subexpression \tcode{E} is +object\iref{customization.point.object}. +Given a subexpression \tcode{E} and +an lvalue \tcode{t} that denotes the same object as \tcode{E}, +if \tcode{E} is an rvalue and +\tcode{enable_safe_range>} is \tcode{false}, +\tcode{ranges::begin(E)} is ill-formed. +Otherwise, +\tcode{ranges::begin(E)} is expression-equivalent to: \begin{itemize} \item - \tcode{E + 0} if \tcode{E} is an lvalue of array type\iref{basic.compound}. + \tcode{t + 0} if \tcode{t} is of array type\iref{basic.compound}. \item - Otherwise, if \tcode{E} is an lvalue, - \tcode{\placeholdernc{decay-copy}(E.begin())} + Otherwise, + \tcode{\placeholdernc{decay-copy}(t.begin())} if it is a valid expression and its type \tcode{I} models \libconcept{input_or_output_iterator}. \item - Otherwise, \tcode{\placeholdernc{decay-copy}(begin(E))} if it is a + Otherwise, \tcode{\placeholdernc{decay-copy}(begin(t))} if it is a valid expression and its type \tcode{I} models \libconcept{input_or_output_iterator} with overload resolution performed in a context that includes the declarations: @@ -347,24 +371,30 @@ \rSec2[range.access.end]{\tcode{ranges::end}} \pnum The name \tcode{ranges::end} denotes a customization point -object\iref{customization.point.object}. The expression -\tcode{ranges::end(E)} for some subexpression \tcode{E} is +object\iref{customization.point.object}. +Given a subexpression \tcode{E} and +an lvalue \tcode{t} that denotes the same object as \tcode{E}, +if \tcode{E} is an rvalue and +\tcode{enable_safe_range>} is \tcode{false}, +\tcode{ranges::end(E)} is ill-formed. +Otherwise, +\tcode{ranges::end(E)} is expression-equivalent to: \begin{itemize} \item - \tcode{E + extent_v} if \tcode{E} is an lvalue of array + \tcode{t + extent_v} if \tcode{E} is of array type\iref{basic.compound} \tcode{T}. \item - Otherwise, if \tcode{E} is an lvalue, - \tcode{\placeholdernc{decay-copy}(E.end())} + Otherwise, + \tcode{\placeholdernc{decay-copy}(t.end())} if it is a valid expression and its type \tcode{S} models \begin{codeblock} sentinel_for \end{codeblock} \item - Otherwise, \tcode{\placeholdernc{decay-copy}(end(E))} if it is a valid + Otherwise, \tcode{\placeholdernc{decay-copy}(end(t))} if it is a valid expression and its type \tcode{S} models \begin{codeblock} sentinel_for @@ -432,17 +462,23 @@ \rSec2[range.access.rbegin]{\tcode{ranges::rbegin}} \pnum The name \tcode{ranges::rbegin} denotes a customization point -object\iref{customization.point.object}. The expression -\tcode{ranges::\brk{}rbegin(E)} for some subexpression \tcode{E} is +object\iref{customization.point.object}. +Given a subexpression \tcode{E} and +an lvalue \tcode{t} that denotes the same object as \tcode{E}, +if \tcode{E} is an rvalue and +\tcode{enable_safe_range>} is \tcode{false}, +\tcode{ranges::rbegin(E)} is ill-formed. +Otherwise, +\tcode{ranges::rbegin(E)} is expression-equivalent to: \begin{itemize} \item - If \tcode{E} is an lvalue, \tcode{\placeholdernc{decay-copy}(E.rbegin())} + \tcode{\placeholdernc{decay-copy}(t.rbegin())} if it is a valid expression and its type \tcode{I} models \libconcept{input_or_output_iterator}. \item - Otherwise, \tcode{\placeholdernc{decay-copy}(rbegin(E))} if it is a valid + Otherwise, \tcode{\placeholdernc{decay-copy}(rbegin(t))} if it is a valid expression and its type \tcode{I} models \libconcept{input_or_output_iterator} with overload resolution performed in a context that includes the declaration: @@ -452,8 +488,8 @@ and does not include a declaration of \tcode{ranges::rbegin}. \item - Otherwise, \tcode{make_reverse_iterator(ranges::end(E))} if both - \tcode{ranges::begin(E)} and \tcode{ranges::end(\brk{}E)} are valid + Otherwise, \tcode{make_reverse_iterator(ranges::end(t))} if both + \tcode{ranges::begin(t)} and \tcode{ranges::end(\brk{}t)} are valid expressions of the same type \tcode{I} which models \libconcept{bidirectional_iterator}\iref{iterator.concept.bidir}. @@ -474,19 +510,25 @@ \rSec2[range.access.rend]{\tcode{ranges::rend}} \pnum The name \tcode{ranges::rend} denotes a customization point -object\iref{customization.point.object}. The expression -\tcode{ranges::rend(E)} for some subexpression \tcode{E} is +object\iref{customization.point.object}. +Given a subexpression \tcode{E} and +an lvalue \tcode{t} that denotes the same object as \tcode{E}, +if \tcode{E} is an rvalue and +\tcode{enable_safe_range>} is \tcode{false}, +\tcode{ranges::rend(E)} is ill-formed. +Otherwise, +\tcode{ranges::rend(E)} is expression-equivalent to: \begin{itemize} \item - If \tcode{E} is an lvalue, \tcode{\placeholdernc{decay-copy}(E.rend())} + \tcode{\placeholdernc{decay-copy}(t.rend())} if it is a valid expression and its type \tcode{S} models \begin{codeblock} sentinel_for \end{codeblock} \item - Otherwise, \tcode{\placeholdernc{decay-copy}(rend(E))} if it is a valid + Otherwise, \tcode{\placeholdernc{decay-copy}(rend(t))} if it is a valid expression and its type \tcode{S} models \begin{codeblock} sentinel_for @@ -499,8 +541,8 @@ and does not include a declaration of \tcode{ranges::rend}. \item - Otherwise, \tcode{make_reverse_iterator(ranges::begin(E))} if both - \tcode{ranges::begin(E)} and \tcode{ranges::\brk{}end(E)} are valid + Otherwise, \tcode{make_reverse_iterator(ranges::begin(t))} if both + \tcode{ranges::begin(t)} and \tcode{ranges::\brk{}end(t)} are valid expressions of the same type \tcode{I} which models \libconcept{bidirectional_iterator}\iref{iterator.concept.bidir}. @@ -744,44 +786,37 @@ \begin{itemdecl} template - concept @\defexposconcept{range-impl}@ = // \expos - requires(T&& t) { - ranges::begin(std::forward(t)); // sometimes equality-preserving (see below) - ranges::end(std::forward(t)); + concept @\deflibconcept{range}@ = + requires(T& t) { + ranges::begin(t); // sometimes equality-preserving (see below) + ranges::end(t); }; - -template - concept @\deflibconcept{range}@ = @\exposconcept{range-impl}@; - -template - concept @\defexposconcept{forwarding-range}@ = // \expos - range && @\exposconcept{range-impl}@; \end{itemdecl} \begin{itemdescr} \pnum The required expressions -\tcode{ranges::begin(std::forward(t))} +\tcode{ranges::begin(t)} and -\tcode{ranges::end(std::forward<\brk{}T>(t))} -of the \exposconcept{range-impl} concept +\tcode{ranges::end(t)} +of the \libconcept{range} concept do not require implicit expression variations\iref{concepts.equality}. \pnum -Given an expression \tcode{E} such that \tcode{decltype((E))} is \tcode{T}, -\tcode{T} models \tcode{\placeholder{range-impl}} only if +Given an expression \tcode{t} such that \tcode{decltype((t))} is \tcode{T\&}, +\tcode{T} models \libconcept{range} only if \begin{itemize} -\item \range{ranges::begin(E)}{ranges::end(E)} +\item \range{ranges::begin(t)}{ranges::end(t)} denotes a range\iref{iterator.requirements.general}, \item both -\tcode{ranges::begin(E)} +\tcode{ranges::begin(t)} and -\tcode{ranges::end(E)} +\tcode{ranges::end(t)} are amortized constant time and non-modifying, and -\item if the type of \tcode{ranges::begin(E)} models -\libconcept{forward_iterator}, \tcode{ranges::begin(E)} is equality-preserving. +\item if the type of \tcode{ranges::begin(t)} models +\libconcept{forward_iterator}, \tcode{ranges::begin(t)} is equality-preserving. \end{itemize} \pnum @@ -796,36 +831,60 @@ might not return equal values or might not be well-defined; \tcode{ranges::begin} should be called at most once for such a range. \end{note} +\end{itemdescr} + +\begin{itemdecl} +template + concept @\deflibconcept{safe_range}@ = + range && + (is_lvalue_reference_v || enable_safe_range>); +\end{itemdecl} +\begin{itemdescr} \pnum -Given an expression \tcode{E} such that \tcode{decltype((E))} is \tcode{T} -and an lvalue \tcode{t} that denotes the same object as \tcode{E}, -\tcode{T} models \exposconcept{forwarding-range} only if -\begin{itemize} -\item \tcode{ranges::begin(E)} and \tcode{ranges::begin(t)} - are expression-equivalent, -\item \tcode{ranges::end(E)} and \tcode{ranges::end(t)} - are expression-equivalent, and -\item the validity of iterators obtained from the object denoted - by \tcode{E} is not tied to the lifetime of that object. -\end{itemize} +Given an expression \tcode{E} such that \tcode{decltype((E))} is \tcode{T}, +\tcode{T} models \libconcept{safe_range} only if +the validity of iterators obtained from the object denoted by \tcode{E} +is not tied to the lifetime of that object. \pnum \begin{note} Since the validity of iterators is not tied to the lifetime of -an object whose type models \exposconceptnc{forwarding-range}, +an object whose type models \libconcept{safe_range}, a function can accept arguments of such a type by value and return iterators obtained from it without danger of dangling. \end{note} +\end{itemdescr} + +\indexlibraryglobal{enable_safe_range}% +\begin{itemdecl} +template + inline constexpr bool enable_safe_range = false; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +Pursuant to \ref{namespace.std}, users may specialize \tcode{enable_safe_range} +for cv-unqualified program-defined types. +Such specializations shall be +usable in constant expressions\iref{expr.const} and +have type \tcode{const bool}. \pnum \begin{example} -Specializations of class template \tcode{subrange}\iref{range.subrange} -model \exposconceptnc{forwarding-range}. \tcode{subrange} provides -non-member rvalue overloads of \tcode{begin} and \tcode{end} with the same -semantics as its member lvalue overloads, and \tcode{subrange}'s iterators --- since they are ``borrowed'' from some other range -- -do not have validity tied to the lifetime of a \tcode{subrange} object. +Each specialization \tcode{S} of class template \tcode{subrange}\iref{range.subrange} +models \libconcept{safe_range} because +\begin{itemize} +\item +\tcode{enable_safe_range} is specialized +to have the value \tcode{true}, and + +\item +\tcode{S}'s iterators +do not have validity tied to the lifetime of an \tcode{S} object +because they are ``borrowed'' from some other range. +\end{itemize} \end{example} \end{itemdescr} @@ -1061,7 +1120,7 @@ \begin{itemdecl} template concept @\deflibconcept{viewable_range}@ = - range && (@\placeholder{forwarding-range}@ || view>); + range && (safe_range || view>); \end{itemdecl} \rSec1[range.utility]{Range utilities} @@ -1267,11 +1326,11 @@ requires (K == subrange_kind::sized); template<@\exposconcept{not-same-as}@ R> - requires @\exposconcept{forwarding-range}@ && - convertible_to, I> && convertible_to, S> + requires safe_range && + convertible_to, I> && convertible_to, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range); - template<@\exposconcept{forwarding-range}@ R> + template requires convertible_to, I> && convertible_to, S> constexpr subrange(R&& r, @\placeholdernc{make-unsigned-like-t}@(iter_difference_t) n) requires (K == subrange_kind::sized) @@ -1310,9 +1369,6 @@ [[nodiscard]] constexpr subrange prev(iter_difference_t n = 1) const requires bidirectional_iterator; constexpr subrange& advance(iter_difference_t n); - - friend constexpr I begin(subrange&& r) { return r.begin(); } - friend constexpr S end(subrange&& r) { return r.end(); } }; template S> @@ -1326,13 +1382,13 @@ subrange(P, @\placeholdernc{make-unsigned-like-t}@(iter_difference_t>)) -> subrange, tuple_element_t<1, P>, subrange_kind::sized>; - template<@\placeholder{forwarding-range}@ R> + template subrange(R&&) -> subrange, sentinel_t, (sized_range || sized_sentinel_for, iterator_t>) ? subrange_kind::sized : subrange_kind::unsized>; - template<@\placeholder{forwarding-range}@ R> + template subrange(R&&, @\placeholdernc{make-unsigned-like-t}@(range_difference_t)) -> subrange, sentinel_t, subrange_kind::sized>; @@ -1398,7 +1454,7 @@ \indexlibraryctor{subrange}% \begin{itemdecl} template<@\exposconcept{not-same-as}@ R> - requires @\exposconcept{forwarding-range}@ && + requires safe_range && convertible_to, I> && convertible_to, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range); \end{itemdecl} @@ -1596,7 +1652,7 @@ does not return an iterator or subrange which could potentially reference a range whose lifetime has ended for a particular rvalue \tcode{range} argument -which does not model \tcode{\placeholder{forwarding-range}}\iref{range.range}. +which does not model \libconcept{safe_range}\iref{range.range}. \indexlibraryglobal{dangling}% \begin{codeblock} namespace std::ranges { @@ -1626,7 +1682,7 @@ before a returned iterator is dereferenced. However, the calls at \#2 and \#3 both return iterators since the lvalue \tcode{vec} and specializations of \tcode{subrange} -model \tcode{\placeholder{forwarding-range}}. +model \libconcept{safe_range}. \end{example} \rSec1[range.factories]{Range factories} @@ -1669,9 +1725,6 @@ static constexpr T* data() noexcept { return nullptr; } static constexpr size_t size() noexcept { return 0; } static constexpr bool empty() noexcept { return true; } - - friend constexpr T* begin(empty_view) noexcept { return nullptr; } - friend constexpr T* end(empty_view) noexcept { return nullptr; } }; } \end{codeblock} @@ -2692,12 +2745,6 @@ constexpr auto data() const requires contiguous_range { return ranges::data(*r_); } - - friend constexpr iterator_t begin(ref_view r) - { return r.begin(); } - - friend constexpr sentinel_t end(ref_view r) - { return r.end(); } }; template ref_view(R&) -> ref_view; diff --git a/source/strings.tex b/source/strings.tex index 6b0aa489af..261601c0cc 100644 --- a/source/strings.tex +++ b/source/strings.tex @@ -3932,6 +3932,9 @@ template> class basic_string_view; + template + inline constexpr bool enable_safe_range> = true; + // \ref{string.view.comparison}, non-member comparison functions template constexpr bool operator==(basic_string_view x, @@ -4033,9 +4036,6 @@ constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; - friend constexpr const_iterator begin(basic_string_view sv) noexcept { return sv.begin(); } - friend constexpr const_iterator end(basic_string_view sv) noexcept { return sv.end(); } - // \ref{string.view.capacity}, capacity constexpr size_type size() const noexcept; constexpr size_type length() const noexcept;