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 theviewviewsspan
andmdspan
. Aspan
is a view over a contiguous sequence of objects, the storage of which is owned by some other object. Anmdspan
is 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]:
span
requires the type ofExtent
to be eitherdynamic_extent_tag
or 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
extent
is not equal todynamic_extent
is_dynamic_dimension_v<Extent> == false
, thenother.size()
shall be equal toextent
.Effects: Constructs a
span
by 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
span
that is a view overCount
elements of the current span starting at elementOffset
. IfCount
is equal todynamic_extent
is_dynamic_dimension<Count> == true
, then aspan
over all elements fromOffset
onwards 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
span
that is a view over all elements fromoffset
onwards.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
span
that is a view overcount
elements of the current span starting at elementoffset
.If count is equal todynamic_extent
, then aspan
over all elements fromoffset
onwards 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; };
dimensions
is 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
dimensions
storage 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.�dimensions
Constructors, Assignment and Destructors [views.dimensions.cons]constexpr dimensions() noexcept;Effects: Constructs a
dimensions
object 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
dimensions
object 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
dimensions
object by copying from anotherdimensions
object.dimensions& operator=(dimensions const& other) noexcept = default; dimensions& operator=(dimensions&& other) noexcept = default;Effects: Assigns from one
dimensions
object into another.
Add the following section, 23.7.� [views.dimensions.obs]:
23.7.�dimensions
Observers [views.dimensions.obs]static constexpr index_type rank() noexcept;Effects: Returns the number of elements in the
dimensions
object.Returns:
sizeof...(Extents)
.static constexpr index_type rank() noexcept;Effects: Returns the number of parameters in
Extents
for whichis_dynamic_dimension_v
istrue
.constexpr bool is_dynamic_dimension(index_type i) const noexcept;Effects: If
i < rank()
, returns true if thei
th dimension is a dynamic dimension. Otherwise, returns false.
Add the following section, 23.7.� [views.dimensions.elem]:
23.7.�dimensions
Element Access [views.dimensions.elem]constexpr value_type operator[](index_type i) const noexcept;Effects: If
i < rank()
, returns the value of thei
th element. Otherwise, returns 0.
Add the following section, 23.7.� [views.dimensions.comparison]:
23.7.�dimensions
Comparison Operators [views.dimensions.comparison]template <auto... Extents> constexpr bool operator==(const dimensions& l, const dimensions & r); Effects:
l[i] == r[i]
, wherei
is 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
mapping
which takes a single template parameter that shall be a type which fulfills the definition of thedimensions
class. Layout mappings are instantiations of this embedded template class.In the following section:
Let
LM
be a layout mapping and letlm
be an object of typeLM
.Let
i
be in the range[0, rank())
in order.The expression
is_layout<LM>::value
shall 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
mdspan
is a multidimensional view over a contiguous sequence of objects, the storage of which is owned by some other object.
ElementType
is required to be a complete object that is not an abstract class type.In the following section:
Let
i
be in the range[0, sizeof...(Properties))
in order and
Pi
be thei
th type inProperties
,Let
r
be in the range[0, rank())
in order and
Idxr
be ther
th type in a template parameter pack namedIndices
,Let
rd
be in the range[0, rank_dynamic())
in order, and
DExtrd
be therd
th type in a template parameter pack namedDynamicExtents
.
is_layout_v<Pi>
shall be true fori
. Thelayout
type is the type inPi
for whichis_layout_v<Pi> == true
. Ifsizeof...(Properties) == 0
oris_layout_v<Pi> == false
for alli
, then thelayout
type islayout_right
.If
is_mdspan_property_v<Pi>
is false for anyi
, thenmdspan
is ill-formed.
Add the following section, 23.7.� [views.mdspan.cons]:
23.7.�mdspan
Constructors, Assignment and Destructors [views.mdspan.cons]constexpr mdspan() noexcept; constexpr mdspan(nullptr_t) noexcept;Remarks: If
!is_dynamic_dimension(r) && extent(r) != 0
then the program is ill-formed.Effects: Constructs an empty
mdspan
.Postconditions:
size() == 0 && data() == nullptr
template <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
ptr
is null, thenl.size()
shall be 0.If
ptr
is not null,ptr
shall point to the beginning of a valid sequence of objects of at leastl.size()
length.Effects: Constructs an
mdspan
that is a view over the sequence of objects pointed to beptr
with mappingl
. Ifptr
is null orl.size()
is 0, then an emptymdspan
is constructed.Complexity: Constant.
Throws: Nothing.
constexpr mdspan(mdspan const& other) noexcept = default; constexpr mdspan(mdspan&& other) noexcept = default;Effects: Constructs an
mdspan
by 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...>::pointer
is implicitly convertible topointer
.Requires: If
!other.is_dynamic_dimension(r)
thenother.extent(r)
shall be equal toextent(r)
, for allr
.Effects: Constructs an
mdspan
by 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
mdspan
to 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...>::pointer
is implicitly convertible topointer
.Requires: If
!other.is_dynamic_dimension(r)
thenother.extent(r)
shall be equal toextent(r)
, for allr
.Effects: Assigns from one
mdspan
to another.Postconditions:
other.size() == size() && other.data() == data() && other.mapping() == mapping()
Add the following section, 23.7.� [views.mdspan.domobs]:
23.7.�mdspan
Domain 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.�mdspan
Codomain 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.�mdspan
Mapping 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.�mdspan
Element 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
D
be an instantiation of thedimensions
class template.
layout_right
shall be a layout ([views.layout.requirements]) such thatlayout_right::mapping<D>().stride(D::rank() - 1) == 1
.
layout_left
shall be a layout such thatlayout_left::mapping<D>().stride(0) == 1
.
3. Feature Testing
The __cpp_lib_mdspan
feature test macro should be added.