1. Introduction
[P0009R12] proposes adding non-owning multidimensional span abstractions
to the C++ Standard Library;
, which is fully generic and
can represent any kind of multidimensional data, and
, a
convenience template alias for simpler use cases.
template < class ElementType , class Extents , class LayoutPolicy = layout_right , class AccessorPolicy = accessor_basic < ElementType >> struct basic_mdspan ; template < class ElementType , size_t ... Extents > using mdspan = basic_mdspan < ElementType , extents < Extents ... >> ;
In the
/
interface, extents can be either static, e.g.
expressed at compile time:
mdspan < double , 64 , 64 > a ( data );
or dynamic, e.g. expressed at run time:
mdspan < double , dynamic_extent , dynamic_extent > a ( data , 64 , 64 );
You can also use a mix of the two styles:
mdspan < double , 64 , dynamic_extent > a ( data , 64 );
While
and
are powerful, the spelling of instantiations
can be verbose, especially for the common case where all extents are dynamic.
2. Class Template Argument Deduction for All Dynamic Extents
Using class template argument deduction (introduced in C++17) and alias
template argument deduction (introduced in C++20), we can make it a easier to
use
with all dynamic extents:
Before | After |
---|---|
|
|
To make this work, we need to add a deduction guide for
.
Through the power of alias template argument deduction,
will be
able to use said deduction guide as well.
Here’s an example implementation of such a
deduction guide for
.
In earlier versions of this paper, it was unclear whether such a deduction
guide would work for the alias template
, as attempts to construct
one that functioned as intended had failed.
However, we have since learned that the this is due to bugs in the two
implementations that currently support alias template deduction, MSVC
and GCC.
Those bugs have been reported to the respective compilers and hopefully
will be fixed soon.
Here’s an example of the current
implementation bugs that prevent the deduction guide from working for
.
3. Template Aliases for All Dynamic Extents
The root of the interface challenge here is the heterogeneous nature of extents
in
.
When you need to express static extents or a mix of static and dynamic extents,
we must state the kind of every extent, similar to how we state the type of
every element of a
.
This heterogenity gives us flexibility at the cost of verbosity.
However, in the case of all dynamic extents, we do not need that flexibility;
the kind of every extent is the same.
Instead of a
-like interface, we likely want an
-like interface,
where we can simple state the number of extents, instead of listing every one.
We can provide this simplified interface easily by adding a template alias for
that takes the number of extents as a parameter and expands to an
of that many
.
Here’s an example implementation.
-like
| -like
|
---|---|
|
|
|
|
If you are working with an
-like representation of extents, working with
can become quite awkward:
template < size_t > constexpr auto make_dynamic_extent () { return dynamic_extent ; } template < typename T , size_t Rank > struct tensor { T * data ; array < size_t , Rank > extents ; auto get_mdspan () { return get_mdspan_impl ( make_index_sequence < Rank > ()); } template < size_t ... Pack > auto get_mdspan_impl ( index_sequence < Pack ... > ) { return mdspan < T , make_dynamic_extent < Pack > ()... > ( data , extents ); } };
With
, this becomes a lot simpler:
template < typename T , size_t Rank > struct tensor { T * data ; array < size_t , Rank > extents ; template < size_t ... Pack > auto get_mdspan_impl ( index_sequence < Pack ... > ) { } };
4. Remove mdspan
Convenience Alias
will have to be used with the unfortunately named
.
I considered proposing something along the lines of
.
However, this would potentially lead to confusion, as a
non-type template
parameter in
would mean "static extent of this size" while a
non-type
template parameter in
would mean "number of extents".
Syntax | Semantics |
---|---|
| with a static size of .
|
| with a single static extent of size .
|
(not proposed)
| with dynamic extents.
|
Instead, I think we should:
-
Delete the
alias.mdspan < T , Extents ... > -
Rename
tobasic_mdspan
.mdspan
The purpose of the
alias was to provide a simple and easy to use
interface for those with less complex and advanced use cases.
When the
/
dichotomy was introduced when the proposal was
first under consideration, before the existence of Class Template Argument
Deduction and the proposed
.
With the combination of these two forces, I believe that a separate
alias and
class are unnecessary to achieve a simple and easy
to use interface.
Before |
|
---|---|
After |
|
Before |
|
After |
|
Before |
|
After |
|
Before |
|
After |
|
Before |
|
After |
|
Before |
|
After |
|
5. Wording
The following changes are relative to the
proposal ([P0009R12]).
The � character is used to denote a placeholder number which shall be selected by the editor.
Modify the header synopsis for
in [mdspan.syn] as follows:
22.7.� Headersynopsis [mdspan.syn]
< mdspan >
namespace std { // [mdspan.extents], class template extents template class extents ; template < size_t Rank > using dextents = decltype ([] < size_t ... Pack > ( index_sequence < Pack ... > ) constexpr { return extents < [] ( auto ) constexpr { return dynamic_extent ; } ( integral_constant < size_t , Pack > {})... > {}; }( make_index_sequence < Rank > {})); // [mdspan.layout], Layout mapping policies class layout_left ; class layout_right ; class layout_stride ; // [mdspan.accessor.basic] template < class ElementType > class default_accessor ; // [mdspan.basic], class template mdspan template < class ElementType , class Extents , class LayoutPolicy = layout_right , class AccessorPolicy = default_accessor < ElementType >> class basic_mdspan ; template < class ElementType , class ... Integrals > explicit basic_mdspan ( ElementType * , Integrals ...) -> basic_mdspan < ElementType , dextents < sizeof ...( Integrals ) > template < class T , size_t ... Extents > using mdspan = basic_mdspan < T , extents < Extents ... >> ; // [mdspan.submdspan] template < class ElementType , class Extents , class LayoutPolicy , class AccessorPolicy , class ... SliceSpecifiers > constexpr basic_mdspan < see below > submdspan ( const basic_mdspan < ElementType , Extents , LayoutPolicy , AccessorPolicy >& m , SliceSpecifiers ... specs ) noexcept ; // tag supporting submdspan struct full_extent_t { explicit full_extent_t () = default ; }; inline constexpr full_extent_t full_extent = full_extent_t {}; }
Replace all occurences of
with
.