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.

4404. Should span(R&&) CTAD apply P2280?

Section: 23.7.2.2.3 [span.deduct] Status: New Submitter: Hewill Kang Opened: 2025-10-04 Last modified: 2025-10-05

Priority: Not Prioritized

View all issues with New status.

Discussion:

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 arrays, 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

Proposed resolution:

This wording is relative to N5014.

  1. Modify 23.7.2.2.1 [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 23.7.2.2.3 [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.