1. Modified P0122R3 Wording
The proposed wording changes are relative to the working draft of the standard as of [N4567] with the changes proposed in [P0122R3] applied.
The � character is used to denote a placeholder section number which the editor shall determine.
Apply the following changes to 23.7.� [views.general] paragraph 1:
The header<span>defines theviewviewsspanandmdspan. Aspanis a view over a contiguous sequence of objects, the storage of which is owned by some other object. Anmdspanis a multidimensional view over a contiguous sequence of objects, the storage of which is owned by some other object.
Apply the following changes to 23.7.�.1 [views.span.synop]:
namespace std { // [views.constants], constantsconstexpr ptrdiff_t dynamic_extent = -1;enum class dynamic_extent_tag {}; inline constexpr dynamic_extent_tag dyn { -1 }; template <auto Extent> using is_dynamic_dimension = typename is_same<decay_t<decltype(Extent)>, dynamic_extent_tag>::type; template <auto Extent> inline constexpr bool is_dynamic_dimension_v = is_dynamic_dimension<Extent>::value; // [views.span], class template span template <class ElementType,ptrdiff_tauto Extent =dynamic_extentdyn> class span; // [views.span.comparison], span comparison operators template <class ElementType,ptrdiff_tauto Extent> constexpr bool operator==(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r); template <class ElementType,ptrdiff_tauto Extent> constexpr bool operator!=(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r); template <class ElementType,ptrdiff_tauto Extent> constexpr bool operator<(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r); template <class ElementType,ptrdiff_tauto Extent> constexpr bool operator<=(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r); template <class ElementType,ptrdiff_tauto Extent> constexpr bool operator>(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r); template <class ElementType,ptrdiff_tauto Extent> constexpr bool operator>=(const span<ElementType, Extent>& l, const span<ElementType, Extent>& r); // [views.span.objectrep], views of object representation template <class ElementType,ptrdiff_tauto Extent> span<const char, ((Extent == dynamic_extentis_dynamic_dimension_v<Extent>) ? dynamic_extent : (sizeof(ElementType) * Extent))> as_bytes(span<ElementType, Extent> s) noexcept; template <class ElementType,ptrdiff_tauto Extent> span<const char, ((Extent == dynamic_extentis_dynamic_dimension_v<Extent>) ? dynamic_extent : (sizeof(ElementType) * Extent))> as_writeable_bytes(span<ElementType, Extent>) noexcept; // [views.dimensions], class template dimensions template <auto... Extents> class dimensions; // [views.dimensions.comparison], dimensions comparison operators template <auto... Extents> constexpr bool operator==(const dimensions& l, const dimensions & r); template <auto... Extents> constexpr bool operator!=(const dimensions & l, const dimensions & r); // [views.mdspan], class template mdspan template <class ElementType, class Dimensions, class... Properties> class mdspan; template <class T> struct is_mdspan_property; template <class T> inline constexpr bool is_mdspan_property_v = is_mdspan_property<T>::value; // [views.layout.props], mdspan layout properties class layout_right; class layout_left; template <class T> struct is_layout; template <class T> inline constexpr bool is_layout_v = is_layout<T>::value; } // namespace std
Add the following paragraph in between paragraphs 2 and 3 in 23.7.�.2 [views.span.template]:
spanrequires the type ofExtentto be eitherdynamic_extent_tagor implicitly convertible to thespan'sindex_type.
Apply the following changes to the definition of span in 23.7.�.2 [views.span.template]
namespace std { // A view over a contiguous, single-dimension sequence of objects template <class ElementType,ptrdiff_tauto Extent =dynamic_extentdyn> class span {
inline constexprstatic index_typeauto extent = Extent;
template <class OtherElementType,ptrdiff_tauto OtherExtent> constexpr span(const span<OtherElementType, OtherExtent>& other); template <class OtherElementType,ptrdiff_tauto OtherExtent> constexpr span(span<OtherElementType, OtherExtent>&& other);
template <ptrdiff_t Offset,ptrdiff_tauto Count =dynamic_extentdyn> constexpr span<element_type, Count> subspan() const; constexpr span<element_type, dynamic_extent> first(index_type count) const; constexpr span<element_type, dynamic_extent> last(index_type count) const; constexpr span<element_type, dynamic_extent> subspan(index_type offset) const; constexpr span<element_type, dynamic_extent> subspan(index_type offset, index_type count= dynamic_extent) const;
Apply the following changes to the converting copy constructor in 23.7.�.3 [views.span.cons]:
template <class OtherElementType,ptrdiff_tauto OtherExtent> constexpr span(const span<OtherElementType, OtherExtent>& other); template <class OtherElementType,ptrdiff_tauto OtherExtent> constexpr span(span<OtherElementType, OtherExtent>&& other);Remarks: These constructors shall not participate in overload resolution unless trying to access OtherElementType through an ElementType* would meet the rules for well-defined object access defined in 3.10/10.
Requires: If
extentis not equal todynamic_extentis_dynamic_dimension_v<Extent> == false, thenother.size()shall be equal toextent.Effects: Constructs a
spanby copying the implementation data members of anotherspan, performing suitable conversions.Postconditions:
size() == other.size() && data() == reinterpret_cast<pointer>(other.data()).Complexity: Constant.
Throws: Nothing.
Apply the following changes to subspan() in 23.7.�.4 [views.span.sub]:
template <ptrdiff_t Offset,ptrdiff_tauto Count =dynamic_extentdyn> constexpr span<element_type, Count> subspan() const;Requires:
(Offset == 0 || Offset > 0 && Offset < size()) && (.Count == dynamic_extentis_dynamic_dimension<Count> || Count >= 0 && Offset + Count <= size())Effects: Returns a new
spanthat is a view overCountelements of the current span starting at elementOffset. IfCountis equal todynamic_extentis_dynamic_dimension<Count> == true, then aspanover all elements fromOffsetonwards is returned.Returns:
span(data() + Offset,.Count == dynamic_extentis_dynamic_dimension<Count> ? size() - Offset : Count)Complexity: Constant.
Add the following subspan() overload to 23.7.�.4 [views.span.sub]:
constexpr span<element_type, dynamic_extent> subspan(index_type offset) const;Requires:
(offset == 0 || offset > 0 && offset < size()).Effects: Returns a new
spanthat is a view over all elements fromoffsetonwards.Returns:
span(data() + offset, size() - offset).Complexity: Constant.
Apply the following changes to subspan() in 23.7.�.4 [views.span.sub]:
constexpr span<element_type, dynamic_extent> subspan(index_type offset, index_type count= dynamic_extent) const;Requires:
(offset == 0 || offset > 0 && offset < size()) && (.count == dynamic_extent ||count >= 0 && offset + count <= size())Effects: Returns a new
spanthat is a view overcountelements of the current span starting at elementoffset.If count is equal todynamic_extent, then aspanover all elements fromoffsetonwards is returned.Returns:
span(data() + offset,.count == dynamic_extent ? size() - offset :count)Complexity: Constant.
2. mdspan Wording
2.1. dimensions Class Template
Add the following section, 23.7.� [views.dimensions.template]:
23.7.� Class Templatedimensions[views.dimensions.template]template <auto... Extents> class dimensions { public: // types using value_type = ptrdiff_t; using index_type = ptrdiff_t; // [views.dimensions.cons], constructors/assignment/destructor constexpr dimensions() noexcept; template <class... DynamicExtents> constexpr dimensions(DynamicExtents... dexts) noexcept; constexpr dimensions(dimensions const& other) noexcept = default; constexpr dimensions(dimensions&& other) noexcept = default; dimensions& operator=(dimensions const& other) noexcept = default; dimensions& operator=(dimensions&& other) noexcept = default; // [views.dimensions.obs], observers static constexpr index_type rank() noexcept; static constexpr index_type rank_dynamic() noexcept; constexpr size_type size() noexcept; constexpr bool is_dynamic_dimension(index_type i) const noexcept; // [views.dimensions.elem], element access constexpr value_type operator[](index_type i) const noexcept; };
dimensionsis a class which contains storage for a fixed number of integer elements, each of which represents the extent of a dimension, collectively forming a multi-dimensional integer index.Each non-type template parameter specifies either a positive integral value, indicating a static dimension, or a value of type
dynamic_extent_tag, indicating a dynamic dimension whose value will be provided at runtime.Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the contained dimensions. The contained values shall be allocated in a region of the
dimensionsstorage suitably aligned for the typevalue_type. [ Note: Implementations are not required to store the value of static dimensions. — end note ]
Add the following section, 23.7.� [views.dimensions.cons]:
23.7.�dimensionsConstructors, Assignment and Destructors [views.dimensions.cons]constexpr dimensions() noexcept;Effects: Constructs a
dimensionsobject with all dynamic dimensions default initialized.template <class... DynamicExtents> constexpr dimensions(DynamicExtents... dexts) noexcept;Remarks: This constructor shall not participate in overload resolution unless
sizeof...(DynamicExtents) == rank_dynamic().Effects: Constructs a
dimensionsobject with each dynamic dimensions initialized from a corresponding value indexts.constexpr dimensions(dimensions const& other) noexcept = default; constexpr dimensions(dimensions&& other) noexcept = default;Effects: Constructs a
dimensionsobject by copying from anotherdimensionsobject.dimensions& operator=(dimensions const& other) noexcept = default; dimensions& operator=(dimensions&& other) noexcept = default;Effects: Assigns from one
dimensionsobject into another.
Add the following section, 23.7.� [views.dimensions.obs]:
23.7.�dimensionsObservers [views.dimensions.obs]static constexpr index_type rank() noexcept;Effects: Returns the number of elements in the
dimensionsobject.Returns:
sizeof...(Extents).static constexpr index_type rank() noexcept;Effects: Returns the number of parameters in
Extentsfor whichis_dynamic_dimension_vistrue.constexpr bool is_dynamic_dimension(index_type i) const noexcept;Effects: If
i < rank(), returns true if theith dimension is a dynamic dimension. Otherwise, returns false.
Add the following section, 23.7.� [views.dimensions.elem]:
23.7.�dimensionsElement Access [views.dimensions.elem]constexpr value_type operator[](index_type i) const noexcept;Effects: If
i < rank(), returns the value of theith element. Otherwise, returns 0.
Add the following section, 23.7.� [views.dimensions.comparison]:
23.7.�dimensionsComparison Operators [views.dimensions.comparison]template <auto... Extents> constexpr bool operator==(const dimensions& l, const dimensions & r); Effects:
l[i] == r[i], whereiis in the range[0, rank()).Throws: Nothing.
template <auto... Extents> constexpr bool operator!=(const dimensions& l, const dimensions & r); Effects: Equivalent to
return !(l == r);
2.2. Layout Mapping Requirements
Add the following section, 23.7.� [views.layout.requirements]:
23.7.� Layout Mapping Requirements [views.layout.requirements]A layout is class that describes a mapping from a multi-dimensional index to a mapped index, which is one-dimensional.
mdspan(23.7.�) is parameterized in terms of layout mappings.A layout shall have an embedded template class
mappingwhich takes a single template parameter that shall be a type which fulfills the definition of thedimensionsclass. Layout mappings are instantiations of this embedded template class.In the following section:
Let
LMbe a layout mapping and letlmbe an object of typeLM.Let
ibe in the range[0, rank())in order.The expression
is_layout<LM>::valueshall be true.The expression
lm.span()shall be well-formed and have the following semantics:
Effects: Returns
(*this)(extent(0), /* ... */, extent(i)) - (*this)(0, /* ... */, 0).The expression
LM::is_always_strided(r)shall be true if the difference between two mapped indices that are consecutive is always identical.If
LM::is_always_strided(r), the expressionlm.stride(r)shall be well-formed and have the following semantics:
Effects: Returns the difference between two mapped indices that are consecutive.
The expression
lm(is...)shall be well-formed and have the following semantics ifsizeof...(decltype(is)) == LM::rank()and all the types indecltype(is)are integral:
Effects: Returns the mapped index for multi-dimensional index
is.
2.3. mdspan Class Template
Add the following section, 23.7.� [views.mdspan.template]:
23.7.� Class templatemdspan[views.mdspan.template]template <class ElementType, class Dimensions, class... Properties> class mdspan { public: // types using element_type = ElementType; using index_type = ptrdiff_t; using difference_type = ptrdiff_t; using pointer = element_type*; using reference = element_type&; using layout = implementation defined; using layout_mapping = typename layout::template mapping; // [views.mdspan.cons], constructors/assignment/destructor constexpr mdspan() noexcept; constexpr mdspan(nullptr_t) noexcept; template <class... DynamicExtents> constexpr mdspan(pointer ptr, DynamicExtents... dexts); constexpr mdspan(pointer ptr, layout_mapping&& l); constexpr mdspan(pointer ptr, layout_mapping const& l); constexpr mdspan(mdspan const& other) noexcept = default; constexpr mdspan(mdspan&& other) noexcept = default; template <class UElementType, class UDimensions, class... UProperties> constexpr mdspan(mdspan<UElementType, UDimensions, UProperties...> const& other); template <class UElementType, class UDimensions, class... UProperties> constexpr mdspan(mdspan<UElementType, UDimensions, UProperties...>&& other); mdspan& operator=(mdspan const& other) noexcept = default; mdspan& operator=(mdspan&& other) noexcept = default; template <class UElementType, class UDimensions, class... UProperties> mdspan& operator=(mdspan<UElementType, UDimensions, UProperties...> const& other); template <class UElementType, class UDimensions, class... UProperties> mdspan& operator=(mdspan<UElementType, UDimensions, UProperties...>&& other); ~mdspan() noexcept = default; // [views.mdspan.domobs], domain observers static constexpr index_type rank() noexcept; static constexpr index_type rank_dynamic() noexcept; constexpr bool is_dynamic_dimension(rank_type r) const noexcept; constexpr index_type extent(rank_type r) const noexcept; constexpr index_type size() const noexcept; constexpr index_type length() const noexcept; constexpr bool empty() const noexcept; // [views.mdspan.codobs], codomain observers constexpr index_type span() const noexcept; // [views.mdspan.mapobs], mapping observers constexpr index_type stride(rank_type r) const noexcept; constexpr layout_mapping mapping() const noexcept; // [views.mdspan.elem], element access template <class... Indices> reference operator()(Indices... is) const; constexpr pointer data() const noexcept; };
An
mdspanis a multidimensional view over a contiguous sequence of objects, the storage of which is owned by some other object.
ElementTypeis required to be a complete object that is not an abstract class type.In the following section:
Let
ibe in the range[0, sizeof...(Properties))in order and
Pibe theith type inProperties,Let
rbe in the range[0, rank())in order and
Idxrbe therth type in a template parameter pack namedIndices,Let
rdbe in the range[0, rank_dynamic())in order, and
DExtrdbe therdth type in a template parameter pack namedDynamicExtents.
is_layout_v<Pi>shall be true fori. Thelayouttype is the type inPifor whichis_layout_v<Pi> == true. Ifsizeof...(Properties) == 0oris_layout_v<Pi> == falsefor alli, then thelayouttype islayout_right.If
is_mdspan_property_v<Pi>is false for anyi, thenmdspanis ill-formed.
Add the following section, 23.7.� [views.mdspan.cons]:
23.7.�mdspanConstructors, Assignment and Destructors [views.mdspan.cons]constexpr mdspan() noexcept; constexpr mdspan(nullptr_t) noexcept;Remarks: If
!is_dynamic_dimension(r) && extent(r) != 0then the program is ill-formed.Effects: Constructs an empty
mdspan.Postconditions:
size() == 0 && data() == nullptrtemplate <class... DynamicExtents> constexpr mdspan(pointer ptr, DynamicExtents... dexts);Effects: Equivalent to
mdspan(ptr, Dimensions(dexts...)).Complexity: Constant.
constexpr mdspan(pointer ptr, layout_mapping&& l); constexpr mdspan(pointer ptr, layout_mapping const& l);Requires:
l.size() >= 0.If
ptris null, thenl.size()shall be 0.If
ptris not null,ptrshall point to the beginning of a valid sequence of objects of at leastl.size()length.Effects: Constructs an
mdspanthat is a view over the sequence of objects pointed to beptrwith mappingl. Ifptris null orl.size()is 0, then an emptymdspanis constructed.Complexity: Constant.
Throws: Nothing.
constexpr mdspan(mdspan const& other) noexcept = default; constexpr mdspan(mdspan&& other) noexcept = default;Effects: Constructs an
mdspanby copying from anothermdspan.Postconditions:
other.size() == size() && other.data() == data() && other.mapping() == mapping()template <class UElementType, class UDimensions, class... UProperties> constexpr mdspan(mdspan<UElementType, UDimensions, UProperties...> const& other); template <class UElementType, class UDimensions, class... UProperties> constexpr mdspan(mdspan<UElementType, UDimensions, UProperties...>&& other);Remarks: This constructor shall not participate in overload resolution unless
mdspan<UElementType, UDimensions, UProperties...>::pointeris implicitly convertible topointer.Requires: If
!other.is_dynamic_dimension(r)thenother.extent(r)shall be equal toextent(r), for allr.Effects: Constructs an
mdspanby copying from anothermdspan.Postconditions:
other.size() == size() && reinterpret_cast<pointer>(other.data()) == data() && other.mapping() == mapping()Complexity: Constant.
Throws: Nothing.
mdspan& operator=(mdspan const& other) noexcept = default; mdspan& operator=(mdspan&& other) noexcept = default;Effects: Assigns from one
mdspanto another.Postconditions:
other.size() == size() && other.data() == data() && other.mapping() == mapping()template <class UElementType, class UDimensions, class... UProperties> mdspan& operator=(mdspan<UElementType, UDimensions, UProperties...> const& other); template <class UElementType, class UDimensions, class... UProperties> mdspan& operator=(mdspan<UElementType, UDimensions, UProperties...>&& other);Remarks: This operator shall not participate in overload resolution unless
mdspan<UElementType, UDimensions, UProperties...>::pointeris implicitly convertible topointer.Requires: If
!other.is_dynamic_dimension(r)thenother.extent(r)shall be equal toextent(r), for allr.Effects: Assigns from one
mdspanto another.Postconditions:
other.size() == size() && other.data() == data() && other.mapping() == mapping()
Add the following section, 23.7.� [views.mdspan.domobs]:
23.7.�mdspanDomain Observers [views.mdspan.domobs]static constexpr index_type rank() noexcept;Effects: Equivalent to
return layout_mapping::rank();static constexpr index_type rank_dynamic() noexcept;Effects: Equivalent to
return layout_mapping::rank_dynamic();constexpr bool is_dynamic_dimension(rank_type r) const noexcept;Effects: Equivalent to
return mapping().is_dynamic_dimension(r);constexpr index_type extent(rank_type r) const noexcept;Effects: Equivalent to
return mapping()[r];constexpr index_type size() const noexcept; constexpr index_type length() const noexcept;Effects: Equivalent to
return mapping().size();constexpr bool empty() const noexcept;Effects: Equivalent to
return size() == 0;
Add the following section, 23.7.� [views.mdspan.codobs]:
23.7.�mdspanCodomain Observers [views.mdspan.codobs]constexpr index_type span() const noexcept;Effects: Equivalent to
return mapping().span();
Add the following section, 23.7.� [views.mdspan.mapobs]:
23.7.�mdspanMapping Observers [views.mdspan.mapobs]constexpr index_type stride(rank_type r) const noexcept;Remarks: This function shall not participate in overload resolution if
layout_mapping::is_always_strided(r)is true for all r.Effects: Equivalent to
return mapping().stride(r);constexpr layout_mapping mapping() const noexcept;Effects: Returns a copy of the
mdspan's layout mapping.
Add the following section, 23.7.� [views.mdspan.mapobs]:
23.7.�mdspanElement Access [views.mdspan.elem]template <class... Indices> reference operator()(Indices... is) const;Effects: Returns a reference to the element at position
mapping()(is...).Complexity: Constant
Throws: Nothing
constexpr pointer data() const noexcept;Effects: If
!empty(), returns a pointer to the first element in the sequence accessible via themdspan. Otherwise, returnsnullptr.Complexity: Constant
2.4. Layout Mapping Properties
Add the following section, 23.7.� [views.layout.props]:
23.7.� Layout Mapping Properties [views.layout.props]In the following section, let
Dbe an instantiation of thedimensionsclass template.
layout_rightshall be a layout ([views.layout.requirements]) such thatlayout_right::mapping<D>().stride(D::rank() - 1) == 1.
layout_leftshall be a layout such thatlayout_left::mapping<D>().stride(0) == 1.
3. Feature Testing
The __cpp_lib_mdspan feature test macro should be added.