This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4401. join_view should be sized_range when applied to ranges of simd::vec

Section: 25.7.14.2 [range.join.view], 25.7.15.2 [range.join.with.view], 25.7.16.2 [range.lazy.split.view] Status: New Submitter: Hewill Kang Opened: 2025-10-02 Last modified: 2025-10-04

Priority: Not Prioritized

View other active issues in [range.join.view].

View all other issues in [range.join.view].

View all issues with New status.

Discussion:

Consider:

Collecting a simd::vec into a vector for output is a common use case. P3480R6 makes simd::vec a range so we can simply flatten it with views::join (original example from the paper):

std::vector<std::simd::vec<float>> data;
auto range_of_float = data | std::views::join;

In this case, it makes sense for join_view to be sized_range because simd::vec::size() is a constant expression that can be multiplied by the original vector size to get the result size of the join_view.

In <ranges>, we use the tiny-range concept to consider types that can obtain static sizes specifically, and simd::vec seems to be a good fit.

Proposed resolution:

This wording is relative to N5014.

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

    [Drafting note: The proposed wording follows the tiny-range's way to check if R::size() is a constant expression instead of further checking ranges::size(r) for simplicity.]

    namespace std::ranges {
      template<auto> struct require-constant;  // exposition only
    
      template<class R>
      concept static-sized-range =             // exposition only
        sized_range<R> &&
        requires { typename require-constant<remove_reference_t<R>::size()>; };
    
      template<input_range V>
        requires view<V> && input_range<range_reference_t<V>>>
      class join_view : public view_interface<join_view<V>> {
      […]
      public:
        […]
        constexpr auto size()
          requires sized_range<V> && static-sized-range<InnerRng> {
          using CT = common_type_t<range_size_t<V>, range_size_t<InnerRng>>;
          return CT(ranges::size(base_)) * CT(remove_reference_t<InnerRng>::size());
        }
    
        constexpr auto size() const
          requires sized_range<const V> &&
                   static-sized-range<range_reference_t<const V>> {
          using InnerConstRng = range_reference_t<const V>;
          using CT = common_type_t<range_size_t<V>, range_size_t<InnerConstRng>>;
          return CT(ranges::size(base_)) * CT(remove_reference_t<InnerConstRng>::size());
        }
      };
      […]
    }
    
  2. Modify 25.7.15.2 [range.join.with.view] as indicated:

    namespace std::ranges {
      […]
      template<input_range V, forward_range Pattern>
        requires view<V> && input_range<range_reference_t<V>>
              && view<Pattern>
              && concatable<range_reference_t<V>, Pattern>
      class join_with_view : public view_interface<join_with_view<V, Pattern>> {
      […]
      public:
        […]
        constexpr auto size()
          requires sized_range<V> && sized_range<Pattern> &&
                   static-sized-range<InnerRng> {
          using CT = common_type_t<
            range_size_t<V>, range_size_t<InnerRng>, range_size_t<Pattern>>;
          const auto base_size = ranges::size(base_);
          if (base_size == 0)
            return CT(0);
          return CT(base_size) * CT(remove_reference_t<InnerRng>::size()) +
                 CT(base_size - 1) * CT(ranges::size(pattern_));
        }
    
        constexpr auto size() const
          requires sized_range<const V> && sized_range<const Pattern> &&
                   static-sized-range<range_reference_t<const V>> {
          using InnerConstRng = range_reference_t<const V>;
          using CT = common_type_t<
            range_size_t<const V>, range_size_t<InnerConstRng>, range_size_t<const Pattern>>;
          const auto base_size = ranges::size(base_);
          if (base_size == 0)
            return CT(0);
          return CT(base_size) * CT(remove_reference_t<InnerConstRng>::size()) +
                 CT(base_size - 1) * CT(ranges::size(pattern_));
        }
      };
      […]
    }
    
  3. Modify 25.7.16.2 [range.lazy.split.view] as indicated:

    namespace std::ranges {
      template<auto> struct require-constant;                       // exposition only
    
      template<class R>
      concept tiny-range =                                          // exposition only
        static-sized-range<R>sized_range<R> &&
        requires { typename require-constant<remove_reference_t<R>::size()>; } &&
        (remove_reference_t<R>::size() <= 1);
      
      […]
    }