| Document number | P3928R1 |
| Date | 2026-06-15 |
| Audience | SG9, LEWG |
| Reply-to | Hewill Kang <hewillk@gmail.com> |
static_sized_range
This paper introduces static_sized_range, a refinement of sized_range for
ranges whose sizes are known at compile time.
It allows detecting and retrieving a range's size as a constant expression through a new consteval
function template
static_size_of.
This enables compile-time reasoning about range sizes and improves constexpr support and optimization
opportunities in range adaptors and algorithms.
Change variable template range_static_size_v to function template based on feetback on SG9 in
Brno.
Add discussion on breaking change, especially CTAD for span.
Initial revision.
The Ranges library initially attempted to identify ranges with compile-time known sizes using an exposition-only
concept, tiny-range, which comes from [range.lazy.split.view]:
template<auto> struct require-constant; // exposition only
template<class R>
concept tiny-range = // exposition only
sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
However, this approach relied on the presence of a static member size() and excluded many types, such
as
span<int, 1>. There was no general mechanism in the language to allow generic code to verify
whether
ranges::size(r) could be evaluated as a constant expression.
The acceptance of P2280 provided a new opportunity: by modifying the rules of constant evaluation, it allows expressions involving references to unknown objects to be considered valid constant expressions when the result does not depend on the object's identity.
This makes ranges::size(r) more generally usable at compile time and allows defining a concept like
static_sized_range in a simpler and more intuitive way.
Additionally, the C++26 simd specification heavily relies on this language enhancement: its wording
repeatedly checks that
ranges::size(r) is a constant expression to ensure, at compile time, the correctness of vector sizes.
However, these compile-time checks and optimizations should not be limited to simd. Introducing the
static_sized_range concept is therefore worthwhile, as it enables compile-time reasoning about range
sizes and broader use in generic programming.
This proposal introduces two related entities: the static_sized_range concept and the
static_size_of function template.
The static_sized_range concept refines sized_range by further requiring that
ranges::size(r) be evaluable as a constant expression. It now can defined as follows:
template<class T>
concept static_sized_range =
sized_range<T> && requires(T& t) { cw<ranges::size(t)>; };
Here, cw enables the formation of a constant_wrapper introduced in P2781 from an expression known to
be a
constant expression,
allowing the concept to directly check whether ranges::size(r) can be evaluated at compile time,
as enabled by P2280.
Once such a range is identified, its compile-time size can be retrieved through a function template:
template<static_sized_range T>
consteval auto static_size_of() {
return decltype([](T& t) { return cw<ranges::size(t)>; }(declval<T&>()))::value;
}
The definition relies on the same mechanism: it promotes the result of ranges::size(r) to a type via
constant_wrapper, and then extracts the value from that type.
This effectively turns a compile-time constant into a type-level entity, allowing the function template to retrieve
the constant directly through ordinary deduction.
Together, static_sized_range and static_size_of provide a minimal yet general
mechanism
for compile-time reasoning about range extents:
static_assert(ranges::static_sized_range<array<int, 3>>); static_assert(ranges::static_size_of<array<int, 3>>() == 3); static_assert(ranges::static_sized_range<span<int, 5>>); static_assert(ranges::static_size_of<span<int, 5>>() == 5); static_assert(ranges::static_sized_range<ranges::single_view<int>>); static_assert(ranges::static_size_of<ranges::single_view<int>>() == 1); static_assert(ranges::static_sized_range<ranges::empty_view<int>>); static_assert(ranges::static_size_of<ranges::empty_view<int>>() == 0); static_assert(!ranges::static_sized_range<span<int>>); static_assert(!ranges::static_sized_range<vector<int>>); static_assert(!ranges::static_sized_range<optional<int>>); static_assert(ranges::static_size_of<string>() == 42); // ill-formed: constraints not satisfied
Although static_size_of<R>() exposes the size of a static_sized_range at compile
time, expressions such as
(static_size_of<R>() >= 1) may not be constant expressions, since
ranges::size(r)
can return an integral-class type lacking constexpr comparison or arithmetic operators.
LWG 4409 highlights this limitation, noting that such types may behave like integers at runtime but are not necessarily usable in constant expressions.
However, it is natural to expect that integer-class types should support compile-time
operations
like built-in integers. To align with this expectation,
it is desirable to clarify the wording for any integer-class type defined in [iterator.concept.winc], ensuring well-defined
constexpr behaviors.
In addition, LWG 4546 indicates that the current standard
does not explicitly specify that integer-class types
are structural types so that they can be used as template parameters, so this part should also be clarified to
ensure the validity of using cw<ranges::size(r)> for constant expression determination.
ref_viewCurrently, ref_view<R>::size() simply forwards to the underlying range via
*r_:
constexpr auto size() const requires sized_range<R>
{ return ranges::size(*r_); }
Even if R satisfies static_sized_range,
this expression is still not a constant expression. As a result, for example,
ref_view<array<int, 42>> does not model static_sized_range.
This can be fixed by having size() return
static_size_of<R>() when R models static_sized_range,
while keeping the original behavior for other ranges:
constexpr auto size() const requires sized_range<R> {
if constexpr (static_sized_range<R>)
return static_size_of<R>();
else
return ranges::size(*r_);
}
Similarly, empty() should receive similar treatment so that
ranges::empty on ref_view can also be a constant expression.
The effect of this change can be seen in the following example:
array a{1, 2, 3, 4, 5};
auto r = a | views::transform([](int i) { return i * i; })
| views::reverse;
static_assert(ranges::size(r) == 5); // ok
static_assert(!ranges::empty(r)); // ok
static_assert(ranges::static_sized_range<decltype(r)>); // ok
static_assert(ranges::static_size_of<decltype(r)>() == 5); // ok
front/back across
array/span/view_interface
For view_interface, the front() and back() members can benefit from
static_sized_range. If a derived view models static_sized_range, calls on an empty range
can be rejected at compile time.
Similarly, array<T, N> and span<T, N> with a fixed extent can take advantage
of this: when N == 0, invoking front() or back() can be diagnosed at compile
time, providing safety without relying on runtime Hardened preconditions added in P3471.
join_view (LWG 4401)LWG 4401 observes that join_view is not
considered a sized_range,
even though in certain cases — such as when the outer range is sized and the inner range has a fixed size
— its total
size can be determined.
With the introduction of static_sized_range and static_size_of, this limitation can
be
addressed more generally:
when the inner range models static_sized_range, the size of a join_view can be computed as
ranges::size(base_) * static_size_of<InnerRng>().
This effectively makes ranges::join_view<span<array<int, 3>, 4>>> a
static_sized_range with a static size of 12.
It is worth noting that this gives join_view the potential to support random-access, which is a
very exciting enhancement, but this requires additional paper for design.
lazy_split_view (LWG 3855, 4108)
LWG 3855 notes that tiny-range is
limited and not fully general.
Using static_sized_range and static_size_of, we can rewrite
tiny-range
with a concept that
correctly captures ranges whose size is known at compile time, and
a range with static_size_of<R>() <= 1 naturally satisfies the intended semantics of
tiny-range
This means that lazy_split_view can now take an array or span of size 1 or 0
as the patterns, which is a nice enhancement:
array a{42};
auto r1 = views::istream<int>(in1) | views::lazy_split(a); // ok
auto r2 = views::istream<int>(in2) | views::lazy_split(array{42}); // ok
auto r3 = views::istream<int>(in3) | views::lazy_split(span{a}); // ok
LWG 4108 observes that lazy_split_view cannot
provide size() for certain valid cases. With static_sized_range, this can be improved:
when the
underlying range is sized and the pattern is statically empty, lazy_split_view can conditionally
provide
size() computed as ranges::size(base_).
span (LWG 4397, 4404)
LWG 4397 concerns constructing a span from a
statically sized range.
The standard currently enforces this with Hardened Preconditions at runtime to reject ranges whose size does
not match the fixed extent.
Applying static_sized_range allows this requirement to be expressed at compile time, which is a useful
enhancement.
LWG
4404 deals with class template argument deduction (CTAD) for
span(R&&).
When the underlying range has a statically known size, the current standard does not uniformly propagate this
information through CTAD due to language rule before P2280. With the newly
static_sized_range, the compiler can
conditionally treat such
span constructions as statically sized, enabling more precise compile-time checks and better
integration with
generic code.
simd/inplace_vector/define_static_array wording (LWG 4396, LWG 4537)
For simd, vector sizes must be known at compile time so that the compiler can generate correct
SIMD
instructions. The standard currently expresses this requirement as "If ranges::size(r) is a constant
expression," which is not easily reusable in generic code.
With the introduction of static_sized_range, this check can be simplified to "If R models
static_sized_range".
This formulation is concise, clearly expresses the intent, and can be applied consistently across the standard
library.
Combined with static_size_of, it provides a uniform mechanism for compile-time size reasoning.
Note that the similar wording in LWG 4396 can also be
simplified with static_sized_range, making the intent explicit and consistent with
simd.
The same wording improvements can also be applied to LWG 4537 for define_static_array.
ranges::min/max/minmaxThose algorithms specify as a Preconditions that the input range must not be empty.
With the introduction of static_sized_range, these preconditions can be
verified at compile time.
For example, invoking ranges::min on
array<int, 0>
could be statically rejected, improving diagnostic clarity and program safety by turning a runtime undefined
behavior into a compile-time error.
Additionally, static_sized_range provides opportunities for compile-time optimizations, with many more
potential enhancements throughout the library.
For example, ranges::equal between two ranges of different static sizes can be evaluated to
false at compile time,
reducing unnecessary instantiations and runtime checks.
optional/inplace_vectorAfter
P3168, optional
could in principle be treated as a tiny-range,
since its maximum size is one. If it were considered
tiny-range, lazy_split_view could use an optional as the pattern
for
input ranges:
auto r = views::istream<int>(in) | views::lazy_split(optional{42});
However, tiny-range is intended to have a fully known, precise size at compile time to support
efficient code
generation, which optional does not provide. For this reason, the author does not adopt such a
treatment.
Additionally, similar to optional, inplace_vector also has a statically known maximum
size. In theory, this could be used to enhance range adaptors such as reserve_hint member introduced in
P2846:
for example, a join_view of a range of optional could have reserve_hint
returning ranges::size(base_) * 1, and ranges::size(base_) * N for range
of inplace_vector<T, N>.
However, this is a very specialized optimization and is not pursued here.
In theory, calls to front, back, or pop_back on an
inplace_vector with zero capacity could be rejected at compile
time. However, this is an extremely limited case and does not justify adding such checks.
We can also do similar checks for append_range/insert_range for
inplace_vector, that is, when the append range is a
static-sized, the size must not be greater than the capacity of inplace_vector. However, this still
does not cover the
situation when the append range is not greater than the capacity but overflows because the current size
of inplace_vector is not a constant expression. However, if LEWG still feels it is worthwhile, the
authors are happy to add wording to it.
One of the improvements proposed in this paper is for the span's CTAD, that is, when a
span takes a
static_sized_range, it will be deduced as a static span, for example:
auto s = views::single(42); auto sp1 = span(s); // span<int> before this proposal, span<int, 1> after this proposal auto e = views::empty<int>; auto sp2 = span(e); // span<int> before this proposal, span<int, 0> after this proposal
However, the author believes this is a worthwhile enhancement because it deduces a more efficient type.
In the standard, the only types that satisfy both static_sized_range and contiguous_range
are raw fixed-size
array, array, static span, and, as mentioned above, empty_view and
single_view. It's worth noting that in the first
three cases, CTAD is already deduced into a static span, leading the authors to believe that the actual
breakage
is relatively small.
As a consequence, the enhancements to ref_view::size in this paper will lead to
define_static_array returning different
types, or make simd::vec accept more range types, because it makes spellings like
a | views::transform(...)
possibly static_sized_range, although the author believes this is the right direction and is willing to
add an appendix if necessary.
This wording is relative to Lastest Working Draft.
Add a new feature-test macro to 17.3.2 [version.syn]:
#define __cpp_lib_ranges_static_sized_range 2026XXL // freestanding, also in <ranges>
Modify 21.4.3 [meta.define.static] as indicated:
template<ranges::input_range R> consteval span<const ranges::range_value_t<R>, see below> define_static_array(R&& r);-16- Effects: Equivalent to:
using T = ranges::range_value_t<R>; meta::info array = meta::reflect_constant_array(r); if (meta::is_array_type(meta::type_of(array))) { return span<const T, see below>(meta::extract<const T*>(array), meta::extent(meta::type_of(array))); } else { return span<const T, see below>(static_cast<const T*>(nullptr), 0); }-?- Remarks: The second template argument of the returned
spantype isstatic_cast<size_t>(ranges::static_size_of<R>()ifranges::size(r))Rmodelsranges::static_sized_range, andranges::size(r)is a constant expressiondynamic_extentotherwise.
Modify 23.2.4 [sequence.reqmts] as indicated:
a.assign_range(rg)-60- Result:
void-61- Mandates:
[…]assignable_from<T&, ranges::range_reference_t<R>>is modeled. Forinplace_vector, ifRmodelsranges::static_sized_rangethenranges::size(rg)is a constant expressionranges::static_size_of<R>()≤ranges::size(rg)a.max_size().a.front();-71- Result:
reference;const_referencefor constanta.-?- Mandates: For
array,a.empty()isfalse.-72- Hardened preconditions:
a.empty()isfalse.[…]
a.back();-75- Result:
reference;const_referencefor constanta.-?- Mandates: For
array,a.empty()isfalse.-76- Hardened preconditions:
a.empty()isfalse.
Modify 23.3.16.2 [inplace.vector.cons] as indicated:
template<container-compatible-range<T> R> constexpr inplace_vector(from_range_t, R&& rg);-?- Mandates: If
Rmodelsranges::static_sized_rangethenranges::size(rg)is a constant expressionranges::static_size_of<R>()≤ranges::size(rg)N.-9- Effects: Constructs an
inplace_vectorwith the elements of the rangerg.-10- Complexity: Linear in
ranges::distance(rg).
Modify 23.7.2.2.2 [span.cons] as indicated:
template<class R> constexpr explicit(extent != dynamic_extent) span(R&& r);-16- Mandates: If
extentis not equal todynamic_extentandRmodelsranges::static_sized_range, thenranges::size(r)is a constant expressionranges::static_size_of<R>()isranges::size(r)== extenttrue.-17- Constraints: Let
Uberemove_reference_t<ranges::range_reference_t<R>>. […]
Modify 23.7.2.2.3 [span.deduct] as indicated:
template<class R> span(R&&) -> see belowspan<remove_reference_t<ranges::range_reference_t<R>>>;-2- Constraints:
Rsatisfiesranges::contiguous_range.-?- Remarks: Let
Tdenote the typeremove_reference_t<ranges::range_reference_t<R>>. The deduced type isspan<T, static_cast<size_t>(ranges::static_size_of<R>())>ifRmodelsranges::static_sized_range, otherwisespan<T>.
Modify 23.7.2.2.6 [span.elem] as indicated:
constexpr reference front() const;-?- Mandates: If
extentis not equal todynamic_extent, thenempty()isfalse.-6- Hardened preconditions:
empty()isfalse.[…]
constexpr reference back() const;-?- Mandates: If
extentis not equal todynamic_extent, thenempty()isfalse.-9- Hardened preconditions:
empty()isfalse.
Modify 24.3.4.4 [iterator.concept.winc] as indicated:
-9- All integer-class types are structural types ([temp.param]) that model
regular([concepts.object]) andthree_way_comparable<strong_ordering>([cmp.concept]).-?- Operations on integer-class type shall be usable in constant expressions.
Modify 25.2 [ranges.syn] as indicated:
// mostly freestanding #include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] #include <iterator> // see [iterator.synopsis] namespace std::ranges { […] // [range.sized], sized ranges template<class> constexpr bool disable_sized_range = false; template<class T> concept approximately_sized_range = see below; template<class T> concept sized_range = see below; // [range.static.sized], static sized ranges template<class T> concept static_sized_range = see below; template<static_sized_range T> consteval auto static_size_of(); […] }
Add 25.4.? Static sized ranges [range.static.sized] after 25.4.4 [range.sized] as indicated:
-1- The
static_sized_rangeconcept refinessized_rangewith the requirement that the number of elements in the range can be determined in compile-time usingranges::static_size_of.template<class T> constexpr bool is-static-sized-range = requires(T& t) { cw<ranges::size(t)>; }; // exposition only template<class T> concept static_sized_range = sized_range<T> && is-static-sized-range<T>; auto cw-size-of(auto& t) -> decltype(cw<ranges::size(t)>); // exposition only template<static_sized_range T> consteval auto static_size_of() { return decltype(cw-size-of(declval<T&>()))::value; }
Modify 25.5.3.2 [view.interface.members] as indicated:
constexpr decltype(auto) front() requires forward_range<D>; constexpr decltype(auto) front() const requires forward_range<const D>;-?- Mandates: Let
Rbedecltype(derived()). IfRmodelsranges::static_sized_range, thenranges::static_size_of<R>() > 0.
-1- Hardened preconditions:
!empty()istrue.-2- Effects: Equivalent to:
return *ranges::begin(derived());
constexpr decltype(auto) back() requires bidirectional_range<D> && common_range<D>; constexpr decltype(auto) back() const requires bidirectional_range<const D> && common_range<const D>;-?- Mandates: Let
Rbedecltype(derived()). IfRmodelsranges::static_sized_range, thenranges::static_size_of<R>() > 0.
-3- Hardened preconditions:
!empty()istrue.
Edit 25.7.6.2 [range.ref.view] as indicated:
namespace std::ranges {
template<range R>
requires is_object_v<R>
class ref_view : public view_interface<ref_view<R>> {
[…]
public:
[…]
constexpr bool empty() const
requires requires { ranges::empty(*r_); } {
if constexpr (static_sized_range<R>)
return static_size_of<R>() == 0;
else
return ranges::empty(*r_);
}
constexpr auto size() const requires sized_range<R> {
if constexpr (static_sized_range<R>)
return static_size_of<R>();
else
return ranges::size(*r_);
}
[…]
};
[…]
}
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>>>
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(static_size_of<InnerRng>());
}
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<const V>, range_size_t<InnerConstRng>>;
return CT(ranges::size(base_)) * CT(static_size_of<InnerConstRng>());
}
};
[…]
}
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(static_size_of<InnerRng>()) +
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(static_size_of<InnerConstRng>()) +
CT(base_size - 1) * CT(ranges::size(pattern_));
}
};
[…]
}
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> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(static_size_of<R>()remove_reference_t<R>::size() <= 1);
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 lazy_split_view::view_interface<lazy_split_view<V, Pattern>> {
[…]
constexpr auto size()
requires sized_range<V> &&
static_sized_range<Pattern> &&
(static_size_of<Pattern>() == 0)
{ return ranges::size(base_); }
constexpr auto size() const
requires sized_range<const V> &&
static_sized_range<Pattern> &&
(static_size_of<Pattern>() == 0)
{ return ranges::size(base_); }
};
[…]
}
Modify 25.7.16.5 [range.lazy.split.inner] as indicated:
constexpr inner-iterator& operator++();-5- Effects: Equivalent to:
incremented_ = true; if constexpr (!forward_range<Base>) { if constexpr (static_size_of<Pattern>Pattern::size() == 0) { return *this; } } ++i_.current; return *this;
Modify 25.7.17.2 [range.split.view] as indicated:
namespace std::ranges {
template<forward_range V, forward_range Pattern>
requires view<V> && view<Pattern> &&
indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to>
class split_view : public view_interface<split_view<V, Pattern>> {
[…]
constexpr auto size()
requires sized_range<V> &&
static_sized_range<Pattern> &&
(static_size_of<Pattern>() == 0) {
return ranges::size(base_);
}
constexpr auto size() const
requires sized_range<const V> &&
static_sized_range<Pattern> &&
(static_size_of<Pattern>() == 0) {
return ranges::size(base_);
}
};
[…]
}
Modify 26.8.9 [alg.min.max] as indicated:
template<class T> constexpr T min(initializer_list<T> r); […] template<execution-policy Ep, sized-random-access-range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> range_value_t<R> ranges::min(Ep&& exec, R&& r, Comp comp = {}, Proj proj = {});[…]-?- Mandates: If
Rmodelsranges::static_sized_range, thenranges::static_size_of<R>() > 0.-5- Preconditions:
ranges::distance(r)>0. […]template<class T> constexpr T max(initializer_list<T> r); […] template<execution-policy Ep, sized-random-access-range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> range_value_t<R> ranges::max(Ep&& exec, R&& r, Comp comp = {}, Proj proj = {});[…]-?- Mandates: If
Rmodelsranges::static_sized_range, thenranges::static_size_of<R>() > 0.-13- Preconditions:
ranges::distance(r)>0. […]template<class T> constexpr pair<T, T> minmax(initializer_list<T> t); […] template<execution-policy Ep, sized-random-access-range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> ranges::minmax_result<range_value_t<R>> ranges::minmax(Ep&& exec, R&& r, Comp comp = {}, Proj proj = {});-?- Mandates: If
Rmodelsranges::static_sized_range, thenranges::static_size_of<R>() > 0.-21- Preconditions:
ranges::distance(r)>0. […]
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class R, class... Flags> constexpr basic_vec(R&& r, flags<Flags...> = {}); template<class R, class... Flags> constexpr basic_vec(R&& r, const mask_type& mask, flags<Flags...> = {});-12- Let
maskbemask_type(true)for the overload with nomaskparameter.-13- Constraints:
[…]
(13.1) —
Rmodelsranges::contiguous_rangeandranges::static_sized_range,
(13.2) —ranges::size(r)is a constant expression,(13.3) —
ranges::static_size_of<R>()is equal toranges::size(r)size(), and(13.4) —
ranges::range_value_t<R>is a vectorizable type and satisfiesexplicitly-convertible-to<T>.template<class R, class... Ts> basic_vec(R&& r, Ts...) -> see below;-17- Constraints:
(17.1) —Rmodelsranges::contiguous_rangeandranges::static_sized_range., and
(17.2) —ranges::size(r)is a constant expression.-18- Remarks: The deduced type is equivalent to
vec<ranges::range_value_t<R>, static_cast<simd-size-type>(.ranges::static_size_of<R>()ranges::size(r))>
Modify 29.10.8.6 [simd.loadstore] as indicated:
template<class V = see below, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> constexpr V unchecked_load(R&& r, flags<Flags...> f = {}); […] template<class V = see below, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> constexpr V unchecked_load(I first, S last, const typename V::mask_type& mask, flags<Flags...> f = {});-1- Let […]
-2- Mandates: If
Rmodelsranges::static_sized_rangethenranges::size(r)is a constant expressionranges::static_size_of<R>()≥ranges::size(r)V::size().
[…]template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> constexpr void unchecked_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); […] template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {});-11- Let […]
-22- Mandates: If
Rmodelsranges::static_sized_rangethenranges::size(r)is a constant expressionranges::static_size_of<R>()≥ranges::size(r)simd-size-v<T, Abi>.