Title
Should span(R&&) CTAD apply P2280?
Status
new
Section
[span.deduct]
Submitter
Hewill Kang

Created on 2025-10-04.00:00:00 last changed 7 days ago

Messages

Date: 2025-10-05.15:19:52

Proposed resolution:

This wording is relative to N5014.

  1. Modify [span.overview] as indicated:

    namespace std {
      template<class ElementType, size_t Extent = dynamic_extent>
      class span {
        […]
      };
      […]
      template<class R>
        span(R&& r) -> see belowspan<remove_reference_t<ranges::range_reference_t<R>>>;
    }
    
  2. Modify [span.deduct] as indicated:

    template<class R>
      span(R&& r) -> see belowspan<remove_reference_t<ranges::range_reference_t<R>>>;
    

    -2- Constraints: `R` satisfies `ranges::contiguous_range`.

    -?- Remarks: Let `T` denote the type remove_reference_t<ranges::range_reference_t<R>>. The deduced type is equivalent to

    1. (?.1) — span<T, static_cast<size_t>(ranges::size(r))> if `R` satisfies `ranges::sized_range` and `ranges::size(r)` is a constant expression.

    2. (?.2) — span<T> otherwise.

Date: 2025-10-04.00:00:00

Thanks to P2280R4, `simd::basic_vec`'s CTAD can specify template parameters directly through `ranges::size`:

basic_vec(R&& r, ...) -> vec<ranges::range_value_t<R>, ranges::size(r)>

However, span with similar CTAD forms do not have this automatic static size optimization applied:

span(R&&) -> span<remove_reference_t<ranges::range_reference_t<R>>>;

Do we need to do it for span?

Note that the `span` constructor actually requires `R` to be a `contiguous_range` and a `sized_range`. If it is further required that `ranges::size` be a constant expression, only raw array, `array`, `span`, `ranges::empty_view`, and `ranges::single_view` satisfy this requirement. Given that `span` already has CTAD for raw arrays and `array`s, this improvement is not significant, but it still seems worthwhile as a compile-time optimization for certain user-defined types or in some specialized cases (demo):

#include <array>
#include <ranges>

constexpr std::size_t N = 42;

auto to_span(auto& r) { 
  static_assert(std::ranges::size(r) == N); // ok after P2280
  return std::span(r);
}

std::array<int, N> a;
auto s1 = to_span(a);
static_assert(std::same_as<decltype(s1), std::span<int, N>>);

auto r = std::array<int, N>{} | std::views::as_const; // as_const_view<owning_view<array<int, N>>>
auto s2 = to_span(r);
static_assert(std::same_as<decltype(s2), std::span<const int, N>>); // fire, ok after this PR
History
Date User Action Args
2025-10-05 15:19:52adminsetmessages: + msg15126
2025-10-04 00:00:00admincreate