Created on 2020-05-26.00:00:00 last changed 12 months ago
Proposed resolution:
This wording is relative to N4861.
Modify [ranges.syn], header <ranges> synopsis, as indicated:
[Drafting note: The project editor is kindly asked to consider replacing editorially all of the
"using Base = conditional_t<Const, const V, V>;" occurrences by "using Base = maybe-const<Const, V>;" ]
[…]
namespace std::ranges {
[…]
namespace views { inline constexpr unspecified filter = unspecified; }
template<bool Const, class T>
using maybe-const = conditional_t<Const, const T, T>; // exposition-only
// [range.transform], transform view
template<input_range V, copy_constructible F>
requires view<V> && is_object_v<F> &&
regular_invocable<F&, range_reference_t<V>>
class transform_view;
[…]
}
Modify [range.transform.sentinel], class template transform_view::sentinel synopsis, as indicated:
[…]namespace std::ranges { template<input_range V, copy_constructible F> requires view<V> && is_object_v<F> && regular_invocable<F&, range_reference_t<V>> && can-reference<invoke_result_t<F&, range_reference_t<V>>> template<bool Const> class transform_view<V, F>::sentinel { […] constexpr sentinel_t<Base> base() const; template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Basemaybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>; template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Basemaybe-const<OtherConst, V>> operator-(const sentinel& y, const iterator<OtherConst>& x)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>; }; }template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);-4- Effects: Equivalent to: return x.current_ == y.end_;
template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Basemaybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;-5- Effects: Equivalent to: return x.current_ - y.end_;
template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Basemaybe-const<OtherConst, V>> operator-(const sentinel& y, const iterator<OtherConst>& x)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;-6- Effects: Equivalent to: return y.end_ - x.current_;
Modify [range.join.sentinel], class template join_view::sentinel synopsis, as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> && (is_reference_v<range_reference_t<V>> || view<range_value_t<V>>) template<bool Const> class join_view<V>::sentinel { […] template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); }; }template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);-3- Effects: Equivalent to: return x.outer_ == y.end_;
[ 2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP. ]
[ 2020-10-02; Status to Tentatively Ready after five positive votes on the reflector ]
[ 2020-08-21 Tim updates PR per telecon discussion ]
As noted in the PR of LWG 3406, the return type of operator- should be based on the constness of the iterator rather than that of the sentinel, as sized_sentinel_for<S, I> ([iterator.concept.sizedsentinel]) requires decltype(i - s) to be iter_difference_t<I>.
[ 2020-07-17; Priority set to 1 in telecon ]
Should be considered together with 3406 and 3449.
Previous resolution [SUPERSEDED]:
This wording is relative to N4861.
Modify [ranges.syn], header <ranges> synopsis, as indicated:
[Drafting note: The project editor is kindly asked to consider replacing editorially all of the
"using Base = conditional_t<Const, const V, V>;" occurrences by "using Base = maybe-const<Const, V>;" ][…] namespace std::ranges { […] namespace views { inline constexpr unspecified filter = unspecified; } template<bool Const, class T> using maybe-const = conditional_t<Const, const T, T>; // exposition-only // [range.transform], transform view template<input_range V, copy_constructible F> requires view<V> && is_object_v<F> && regular_invocable<F&, range_reference_t<V>> class transform_view; […] }
Modify [range.transform.sentinel], class template transform_view::sentinel synopsis, as indicated:
[…]namespace std::ranges { template<input_range V, copy_constructible F> requires view<V> && is_object_v<F> && regular_invocable<F&, range_reference_t<V>> && can-reference<invoke_result_t<F&, range_reference_t<V>>> template<bool Const> class transform_view<V, F>::sentinel { […] constexpr sentinel_t<Base> base() const; template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Base> operator-(const iterator<OtherConst>& x, const sentinel& y)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>; template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Base> operator-(const sentinel& y, const iterator<OtherConst>& x)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>; }; }template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);-4- Effects: Equivalent to: return x.current_ == y.end_;
template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Base> operator-(const iterator<OtherConst>& x, const sentinel& y)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;-5- Effects: Equivalent to: return x.current_ - y.end_;
template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<Base> operator-(const sentinel& y, const iterator<OtherConst>& x)requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;-6- Effects: Equivalent to: return y.end_ - x.current_;
Modify [range.join.sentinel], class template join_view::sentinel synopsis, as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> && (is_reference_v<range_reference_t<V>> || view<range_value_t<V>>) template<bool Const> class join_view<V>::sentinel { […] template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); }; }template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);-3- Effects: Equivalent to: return x.outer_ == y.end_;
A user reported that this doesn't compile:
#include <list> #include <ranges> std::list v{1, 2}; // works if std::vector auto view1 = v | std::views::take(2); auto view2 = view1 | std::views::transform([] (int i) { return i; }); bool b = std::ranges::cbegin(view2) == std::ranges::end(view2);
The comparison is supposed to use operator==(iterator<Const>, sentinel<Const>) after converting sentinel<false> to sentinel<true>. However, the operator== is a hidden friend so is not a candidate when comparing iterator<true> with sentinel<false>. The required conversion would only happen if we'd found the operator, but we can't find the operator until after the conversion happens.
As Patrick noted, the join_view sentinel has a similar problem. The proposed wording shown below has been suggested by Casey and has been implemented and tested in GCC's libstdc++.History | |||
---|---|---|---|
Date | User | Action | Args |
2023-11-22 15:47:43 | admin | set | status: wp -> c++23 |
2020-11-09 21:40:50 | admin | set | messages: + msg11577 |
2020-11-09 21:40:50 | admin | set | status: ready -> wp |
2020-10-02 17:02:44 | admin | set | messages: + msg11496 |
2020-10-02 17:02:44 | admin | set | status: new -> ready |
2020-08-22 01:49:00 | admin | set | messages: + msg11456 |
2020-07-17 22:37:26 | admin | set | messages: + msg11399 |
2020-05-28 18:45:19 | admin | set | messages: + msg11309 |
2020-05-26 00:00:00 | admin | create |