Title
Nesting join_views is broken because of CTAD
Status
c++23
Section
[range.join]
Submitter
Barry Revzin

Created on 2020-08-04.00:00:00 last changed 13 months ago

Messages

Date: 2020-11-09.21:40:50

Proposed resolution:

This wording is relative to N4861.

  1. Modify [range.join.overview] as indicated:

    -2- The name views::join denotes a range adaptor object ([range.adaptor.object]). Given a subexpression E, the expression views::join(E) is expression-equivalent to join_view<views::all_t<decltype((E))>>{E}.

Date: 2020-11-09.00:00:00

[ 2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP. ]

Date: 2020-08-15.00:00:00

[ 2020-08-21; Issue processing telecon: Option A is Tentatively Ready ]

Previous resolution [SUPERSEDED]:

This wording is relative to N4861.

[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]

Option A:

  1. Modify [range.join.overview] as indicated:

    -2- The name views::join denotes a range adaptor object ([range.adaptor.object]). Given a subexpression E, the expression views::join(E) is expression-equivalent to join_view<views::all_t<decltype((E))>>{E}.

Option B:

  1. Modify [range.join.view] as indicated:

    namespace std::ranges {
      […]
      
      template<class R>
        explicit join_view(R&&) -> join_view<views::all_t<R>>;
      
      template<class V>
        explicit join_view(join_view<V>) -> join_view<join_view<V>>;
    }
    
Date: 2020-08-04.00:00:00

Let's say I had a range of range of ranges and I wanted to recursively flatten it. That would involve repeated invocations of join. But this doesn't work:

std::vector<std::vector<std::vector<int>>> nested_vectors = {
  {{1, 2, 3}, {4, 5}, {6}},
  {{7},       {8, 9}, {10, 11, 12}},
  {{13}}
};
auto joined = nested_vectors | std::views::join | std::views::join;

The expectation here is that the value_type of joined is int, but it's actually vector<int> — because the 2nd invocation of join ends up just copying the first. This is because join is specified to do:

The name views::join denotes a range adaptor object ([range.adaptor.object]). Given a subexpression E, the expression views::join(E) is expression-equivalent to join_view{E}.

And join_view{E} for an E that's already a specialization of a join_view just gives you the same join_view back. Yay CTAD. We need to do the same thing with join that we did with reverse in P1252. We can do that either in exposition (Option A) my modifying [range.join.overview] p2

The name views::join denotes a range adaptor object ([range.adaptor.object]). Given a subexpression E, the expression views::join(E) is expression-equivalent to join_view<views::all_t<decltype((E))>>{E}.

Or in code (Option B) add a deduction guide to [range.join.view]:

  template<class R>
    explicit join_view(R&&) -> join_view<views::all_t<R>>;

  template<class V>
    explicit join_view(join_view<V>) -> join_view<join_view<V>>;

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2020-11-09 21:40:50adminsetmessages: + msg11588
2020-11-09 21:40:50adminsetstatus: ready -> wp
2020-08-21 20:18:09adminsetmessages: + msg11446
2020-08-21 20:18:09adminsetstatus: new -> ready
2020-08-09 10:21:26adminsetmessages: + msg11434
2020-08-04 00:00:00admincreate