This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.
Section: 26.5.4 [range.subrange] Status: C++20 Submitter: Eric Niebler Opened: 2019-09-10 Last modified: 2021-02-25
Priority: 1
View all other issues in [range.subrange].
View all issues with C++20 status.
Discussion:
The following code leads to slicing and general badness:
struct Base {}; struct Derived : Base {}; subrange<Derived*> sd; subrange<Base*> sb = sd;
Traversal operations on iterators that are pointers do pointer arithmetic. If a Base* is actually pointing to a Derived*, then pointer arithmetic is invalid. subrange's constructors can easily flag this invalid code, and probably should.
The following PR incorporates the suggested fix to issue LWG 3281 I previously reported. Suggested priority: P1, since it will be hard to fix this after C++20 ships.[2019-10 Priority set to 1 and status to LEWG after reflector discussion]
[2019-10; Marshall comments]
This issue would resolve US-285.
[2019-11 LEWG says OK; Status to Open. Friday PM discussion in Belfast. Casey to investigate and report back.]
Previous resolution [SUPERSEDED]:
This wording is relative to N4830.
Modify 26.5.4 [range.subrange] as indicated:
namespace std::ranges { template<class From, class To> concept convertible-to-non-slicing = // exposition only convertible_to<From, To> && !(is_pointer_v<decay_t<From>> && is_pointer_v<decay_t<To>> && not-same-as<remove_pointer_t<decay_t<From>>, remove_pointer_t<decay_t<To>>>); template<class T> concept pair-like = // exposition only […]template<class T, class U, class V> concept pair-like-convertible-to = // exposition only !range<T> && pair-like<remove_reference_t<T>> && requires(T&& t) { { get<0>(std::forward<T>(t)) } -> convertible_to<U>; { get<1>(std::forward<T>(t)) } -> convertible_to<V>; };template<class T, class U, class V> concept pair-like-convertible-from = // exposition only !range<T> && pair-like<T> && constructible_from<T, U, V> && convertible-to-non-slicing<U, tuple_element_t<0, T>> && convertible_to<V, tuple_element_t<1, T>>; […] […] template<input_or_output_iterator I, sentinel_for<I> S = I, subrange_kind K = sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized> requires (K == subrange_kind::sized || !sized_sentinel_for<S, I>) class subrange : public view_interface<subrange<I, S, K>> { private: […] public: subrange() = default; constexpr subrange(convertible-to-non-slicing<I> auto i, S s) requires (!StoreSize); constexpr subrange(convertible-to-non-slicing<I> auto i, S s, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized); template<not-same-as<subrange> R> requires forwarding-range<R> &&convertible_toconvertible-to-non-slicing<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>); template<forwarding-range R> requires convertible_to<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized) : subrange{ranges::begin(r), ranges::end(r), n} {}template<not-same-as<subrange> PairLike> requires pair-like-convertible-to<PairLike, I, S> constexpr subrange(PairLike&& r) requires (!StoreSize) : subrange{std::get<0>(std::forward<PairLike>(r)), std::get<1>(std::forward<PairLike>(r))} {} template<pair-like-convertible-to<I, S> PairLike> constexpr subrange(PairLike&& r, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized) : subrange{std::get<0>(std::forward<PairLike>(r)), std::get<1>(std::forward<PairLike>(r)), n} {}[…] }; template<input_or_output_iterator I, sentinel_for<I> S> subrange(I, S) -> subrange<I, S>; […] }Modify 26.5.4.2 [range.subrange.ctor] as indicated:
constexpr subrange(convertible-to-non-slicing<I> auto i, S s) requires (!StoreSize);-1- Expects: […]
[…]constexpr subrange(convertible-to-non-slicing<I> auto i, S s, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized);-2- Expects: […]
[…]template<not-same-as<subrange> R> requires forwarding-range<R> &&convertible_toconvertible-to-non-slicing<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>);-6- Effects: […]
[…]
[2020-02-10; Prague]
The group identified minor problems that have been fixed in the revised wording.
[2020-02-10 Move to Immediate Monday afternoon in Prague]
Proposed resolution:
This wording is relative to N4830.
Modify 26.5.4 [range.subrange] as indicated:
namespace std::ranges { template<class From, class To> concept convertible-to-non-slicing = // exposition only convertible_to<From, To> && !(is_pointer_v<decay_t<From>> && is_pointer_v<decay_t<To>> && not-same-as<remove_pointer_t<decay_t<From>>, remove_pointer_t<decay_t<To>>>); template<class T> concept pair-like = // exposition only […]template<class T, class U, class V> concept pair-like-convertible-to = // exposition only !range<T> && pair-like<remove_reference_t<T>> && requires(T&& t) { { get<0>(std::forward<T>(t)) } -> convertible_to<U>; { get<1>(std::forward<T>(t)) } -> convertible_to<V>; };template<class T, class U, class V> concept pair-like-convertible-from = // exposition only !range<T> && pair-like<T> && constructible_from<T, U, V> && convertible-to-non-slicing<U, tuple_element_t<0, T>> && convertible_to<V, tuple_element_t<1, T>>; […] […] template<input_or_output_iterator I, sentinel_for<I> S = I, subrange_kind K = sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized> requires (K == subrange_kind::sized || !sized_sentinel_for<S, I>) class subrange : public view_interface<subrange<I, S, K>> { private: […] public: subrange() = default; constexpr subrange(convertible-to-non-slicing<I> auto i, S s) requires (!StoreSize); constexpr subrange(convertible-to-non-slicing<I> auto i, S s, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized); template<not-same-as<subrange> R> requires forwarding-range<R> &&convertible_toconvertible-to-non-slicing<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>); template<forwarding-range R> requiresconvertible_toconvertible-to-non-slicing<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized) : subrange{ranges::begin(r), ranges::end(r), n} {}template<not-same-as<subrange> PairLike> requires pair-like-convertible-to<PairLike, I, S> constexpr subrange(PairLike&& r) requires (!StoreSize) : subrange{std::get<0>(std::forward<PairLike>(r)), std::get<1>(std::forward<PairLike>(r))} {} template<pair-like-convertible-to<I, S> PairLike> constexpr subrange(PairLike&& r, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized) : subrange{std::get<0>(std::forward<PairLike>(r)), std::get<1>(std::forward<PairLike>(r)), n} {}[…] }; template<input_or_output_iterator I, sentinel_for<I> S> subrange(I, S) -> subrange<I, S>; […] }
Modify 26.5.4.2 [range.subrange.ctor] as indicated:
constexpr subrange(convertible-to-non-slicing<I> auto i, S s) requires (!StoreSize);-1- Expects: […]
[…]constexpr subrange(convertible-to-non-slicing<I> auto i, S s, make-unsigned-like-t(iter_difference_t<I>) n) requires (K == subrange_kind::sized);-2- Expects: […]
[…]template<not-same-as<subrange> R> requires forwarding-range<R> &&convertible_toconvertible-to-non-slicing<iterator_t<R>, I> && convertible_to<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>);-6- Effects: […]
[…]