Document #: | P2367R0 |
Date: | 2021-04-29 |
Project: | Programming Language C++ |
Audience: |
LWG |
Reply-to: |
Tim Song <t.canens.cpp@gmail.com> |
This paper provides wording for [LWG3524] and resolves related issues caused by the erroneous use of list-initialization in ranges wording.
As discussed in [LWG3524], the use of list-initialization in the ranges specification implies ordering guarantees that are unintended and unimplementable in ordinary C++, as well as narrowing checks that are unnecessary and sometimes unimplementable.
Other issues have also been reported against the misuse of list-initialization in ranges wording. See [subrange] and [take.view].
The approach taken in this paper is to remove uses of list-initialization that are actively harmful, as well as any neighboring uses to maintain some level of local consistency. I have not attempted to remove all uses of list-initialization in the normative text of Clause 24.
During the drafting of this paper, two issues were discovered with views::single
:
views::single(a_single_view)
unexpectedly copies instead of wraps, similar to [LWG3474].The proposed wording below fixes these issues as well.
This wording is relative to [N4885].
template<not-same-as<subrange> R> requires borrowed_range<R> && convertible-to-non-slicing<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>);
6 Effects: Equivalent to:
2 The name
views::single
denotes a customization point object (16.3.3.3.6 [customization.point.object]). Given a subexpressionE
, the expressionviews::single(E)
is expression-equivalent tosingle_view<decay_t<decltype((E))>>{(E})
.
2 The name
views::iota
denotes a customization point object (16.3.3.3.6 [customization.point.object]). Given subexpressionsE
andF
, the expressionsviews::iota(E)
andviews::iota(E, F)
are expression-equivalent toiota_view{(E})
andiota_view{(E, F})
, respectively.
2 The name
views::filter
denotes a range adaptor object (24.7.2 [range.adaptor.object]). Given subexpressionsE
andP
, the expressionviews::filter(E, P)
is expression-equivalent tofilter_view{(E, P})
.
2 The name
views::transform
denotes a range adaptor object (24.7.2 [range.adaptor.object]). Given subexpressionsE
andF
, the expressionviews::transform(E, F)
is expression-equivalent totransform_view{(E, F})
.
2 The name
views::take
denotes a range adaptor object (24.7.2 [range.adaptor.object]). LetE
andF
be expressions, letT
beremove_cvref_t<decltype((E))>
, and letD
berange_difference_t<decltype((E))>
. Ifdecltype((F))
does not modelconvertible_to<D>
,views::take(E, F)
is ill-formed. Otherwise, the expressionviews::take(E, F)
is expression-equivalent to:
(2.1) If T is a specialization of
ranges::empty_view
(24.6.2.2 [range.empty.view]), then((void) F, decay-copy(E))
, except that the evaluations ofE
andF
are indeterminately sequenced.(2.2) Otherwise, if
T
modelsrandom_access_range
andsized_range
and is
- (2.2.1) […]
then
T{(ranges::begin(E), ranges::begin(E) + min<D>(ranges::size(E), F)})
, except thatE
is evaluated only once.(2.3) Otherwise,
ranges::take_view{(E, F})
.
namespace std::ranges { template<view V> class take_view : public view_interface<take_view<V>> { // [...] constexpr auto begin() requires (!simple-view<V>) { if constexpr (sized_range<V>) { if constexpr (random_access_range<V>) return ranges::begin(base_); else { auto sz = size(); return counted_iterator{(ranges::begin(base_), sz}); } } else return counted_iterator{(ranges::begin(base_), count_}); } constexpr auto begin() const requires range<const V> { if constexpr (sized_range<const V>) { if constexpr (random_access_range<const V>) return ranges::begin(base_); else { auto sz = size(); return counted_iterator{(ranges::begin(base_), sz}); } } else return counted_iterator{(ranges::begin(base_), count_}); } // [...] }; template<class R> take_view(R&&, range_difference_t<R>) -> take_view<views::all_t<R>>; }
2 The name
views::take_while
denotes a range adaptor object (24.7.2 [range.adaptor.object]). Given subexpressionsE
andF
, the expressionviews::take_while(E, F)
is expression-equivalent totake_while_view{(E, F})
.
2 The name
views::drop
denotes a range adaptor object (24.7.2 [range.adaptor.object]). LetE
andF
be expressions, letT
beremove_cvref_t<decltype((E))>
, and letD
berange_difference_t<decltype((E))>
. Ifdecltype((F))
does not modelconvertible_to<D>
,views::drop(E, F)
is ill-formed. Otherwise, the expressionviews::drop(E, F)
is expression-equivalent to:
(2.1) If T is a specialization of
ranges::empty_view
(24.6.2.2 [range.empty.view]), then((void) F, decay-copy(E))
, except that the evaluations ofE
andF
are indeterminately sequenced.(2.2) Otherwise, if
T
modelsrandom_access_range
andsized_range
and is
- (2.2.1) […]
then
T{(ranges::begin(E) + min<D>(ranges::size(E), F), ranges::end(E)})
, except thatE
is evaluated only once.(2.3) Otherwise,
ranges::drop_view{(E, F})
.
2 The name
views::drop_while
denotes a range adaptor object (24.7.2 [range.adaptor.object]). Given subexpressionsE
andF
, the expressionviews::drop_while(E, F)
is expression-equivalent todrop_while_view{(E, F})
.
2 The name
views::split
denotes a range adaptor object (24.7.2 [range.adaptor.object]). Given subexpressionsE
andF
, the expressionviews::split(E, F)
is expression-equivalent tosplit_view{(E, F})
.
template<input_range R> requires constructible_from<V, views::all_t<R>> && constructible_from<Pattern, single_view<range_value_t<R>>> constexpr split_view(R&& r, range_value_t<R> e);
2 Effects: Initializes
base_
withviews::all(std::forward<R>(r))
, andpattern_
withsingle_view{std::move(e)}
views::single(std::move(e))
.
2 The name
views::counted
denotes a customization point object (16.3.3.3.6 [customization.point.object]). LetE
andF
be expressions, letT
bedecay_t<decltype((E))>
, and letD
beiter_difference_t<T>
. Ifdecltype((F))
does not modelconvertible_to<D>
,views::counted(E, F)
is ill-formed.[ Note 1: This case can result in substitution failure when
views::counted(E, F)
appears in the immediate context of a template instantiation. — end note ]Otherwise,
views::counted(E, F)
is expression-equivalent to:
[LWG3474] Barry Revzin. Nesting join_views is broken because of CTAD.
https://wg21.link/lwg3474
[LWG3524] Tim Song. Unimplementable narrowing and evaluation order requirements for range adaptors.
https://wg21.link/lwg3524
[N4885] Thomas Köppe. 2021-03-17. Working Draft, Standard for Programming Language C++.
https://wg21.link/n4885
[subrange] Jonathan Wakely. 2020. Yet Another Braced Init Bug.
https://lists.isocpp.org/lib/2020/10/17806.php
[take.view] Michael Schellenberger Costa. 2021. Fix error in take_view::begin
by casting to the appropriate size.
https://github.com/microsoft/STL/pull/1844