Section: 25.7.8.2 [range.filter.view], 25.7.9.2 [range.transform.view], 25.7.10.2 [range.take.view], 25.7.14.2 [range.join.view], 25.7.17.2 [range.split.view], 25.7.21.2 [range.reverse.view] Status: C++20 Submitter: Eric Niebler Opened: 2019-09-09 Last modified: 2021-02-25
Priority: 1
View all issues with C++20 status.
Discussion:
The following program fails to compile:
#include <ranges> int main() { namespace ranges = std::ranges; int a[] = {1, 7, 3, 6, 5, 2, 4, 8}; auto r0 = ranges::view::reverse(a); auto is_even = [](int i) { return i % 2 == 0; }; auto r1 = ranges::view::filter(r0, is_even); int sum = 0; for (auto i : r1) { sum += i; } return sum - 20; }
The problem comes from constraint recursion, caused by the following constructor:
template<viewable_range R> requires bidirectional_range<R> && constructible_from<V, all_view<R>> constexpr explicit reverse_view(R&& r);
This constructor owes its existence to class template argument deduction; it is the constructor
we intend to use to resolve reverse_view{r}
, which (in accordance to the deduction guide)
will construct an object of type reverse_view<all_view<decltype(r)>>
.
all_view<R>
is always one of:
decay_t<R>
ref_view<remove_reference_t<R>>
subrange<iterator_t<R>, sentinel_t<R>, [sized?]>
In all cases, there is a conversion from r
to the destination type. As a result, the
following non-template reverse_view
constructor can fulfill the duty that the above
constructor was meant to fulfill, and does not cause constraint recursion:
constexpr explicit reverse_view(V r);
In short, the problematic constructor can simply be removed with no negative impact on the design. And the similar constructors from the other range adaptors should similarly be stricken.
Suggested priority P1. The view types are unusable without this change. This proposed resolution has been implemented in range-v3 and has been shipping for some time.[2019-10 Priority set to 1 after reflector discussion]
[2019-10 Status set to ready Wednesday night discussion in Belfast.]
Proposed resolution:
This wording is relative to N4830.
Modify 25.7.8.2 [range.filter.view] as indicated:
namespace std::ranges { template<input_range V, indirect_unary_predicate<iterator_t<V>> Pred> requires view<V> && is_object_v<Pred> class filter_view : public view_interface<filter_view<V, Pred>> { […] public: filter_view() = default; constexpr filter_view(V base, Pred pred);[…]template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr filter_view(R&& r, Pred pred);[…] }; […] }template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr filter_view(R&& r, Pred pred);
-2- Effects: Initializesbase_
withviews::all(std::forward<R>(r))
and initializespred_
withstd::move(pred)
.
Modify 25.7.9.2 [range.transform.view] 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>> class transform_view : public view_interface<transform_view<V, F>> { private: […] public: transform_view() = default; constexpr transform_view(V base, F fun);[…]template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr transform_view(R&& r, F fun);[…] }; […] }template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr transform_view(R&& r, F fun);
-2- Effects: Initializesbase_
withviews::all(std::forward<R>(r))
andfun_
withstd::move(fun)
.
Modify 25.7.10.2 [range.take.view] as indicated:
namespace std::ranges { template<view V> class take_view : public view_interface<take_view<V>> { private: […] public: take_view() = default; constexpr take_view(V base, range_difference_t<V> count);[…]template<viewable_range R> requires constructible_from<V, all_view<R>> constexpr take_view(R&& r, range_difference_t<V> count);[…] }; […] }template<viewable_range R> requires constructible_from<V, all_view<R>> constexpr take_view(R&& r, range_difference_t<V> count);
-2- Effects: Initializesbase_
withviews::all(std::forward<R>(r))
andcount_
withcount
.
Modify 25.7.14.2 [range.join.view] 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>>) class join_view : public view_interface<join_view<V>> { private: […] public: join_view() = default; constexpr explicit join_view(V base);[…]template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr explicit join_view(R&& r);[…] }; […] }template<input_range R> requires viewable_range<R> && constructible_from<V, all_view<R>> constexpr explicit join_view(R&& r);
-2- Effects: Initializesbase_
withviews::all(std::forward<R>(r))
.
Modify 25.7.17.2 [range.split.view] as indicated:
namespace std::ranges { […] template<input_range V, forward_range Pattern> requires view<V> && view<Pattern> && indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_range<V> || tiny-range<Pattern>) class split_view : public view_interface<split_view<V, Pattern>> { private: […] public: split_view() = default; constexpr split_view(V base, Pattern pattern);[…]template<input_range R, forward_range P> requires constructible_from<V, all_view<R>> && constructible_from<Pattern, all_view<P>> constexpr split_view(R&& r, P&& p);[…] }; […] }template<input_range R, forward_range P> requires constructible_from<V, all_view<R>> && constructible_from<Pattern, all_view<P>> constexpr split_view(R&& r, P&& p);
-2- Effects: Initializesbase_
withviews::all(std::forward<R>(r))
, andpattern_
withviews::all(std::forward<P>(p))
.
Modify 25.7.21.2 [range.reverse.view] as indicated:
namespace std::ranges { template<view V> requires bidirectional_range<V> class reverse_view : public view_interface<reverse_view<V>> { private: […] public: reverse_view() = default; constexpr explicit reverse_view(V r);[…]template<viewable_range R> requires bidirectional_range<R> && constructible_from<V, all_view<R>> constexpr explicit reverse_view(R&& r);[…] }; […] }template<viewable_range R> requires bidirectional_range<R> && constructible_from<V, all_view<R>> constexpr explicit reverse_view(R&& r);
-2- Effects: Initializesbase_
withviews::all(std::forward<R>(r))
.