1. Introduction
[P0009R10]'s
is a convenience template alias for simpler use cases of
:
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 );
2. Problem
The [P0009R10] interface style for expressing extents currently interacts poorly with Class Template Argument Deduction (CTAD). As of C++20, CTAD now works with template aliases, so you can write this:
mdspan a ( data , 64 , 64 );
This syntax would be very nice.
It would remove the need to explicitly spell out the verbose
when dealing with run time extents, which I believe is the common case for
most users.
However, the above code does not appear to do what you might expect.
This appears to instantiate
, and then
; a multi-dimensional array of rank 0.
This will lead to a static assertion as
's dynamic extent
constructor. You can see the code on Godbolt here.
3. Solutions
3.1. Make mdspan
A Template Class
If
was a template class, not a template alias, we could simply add
a deduction guide to handle this:
template < class ElementType , class ... IndexType > explicit mdspan ( ElementType * , IndexType ...) -> mdspan < ElementType , [] ( auto ) constexpr { return dynamic_extent ; } ( identity < IndexType > {})... > ;
However, it seems you cannot add a deduction guide for a template alias.
Perhaps there is a way of formulating a deduction guide for
that
would help here, however I could not find a way of doing this.
One way we could solve this problem would be to make
a template class,
not a template alias, and then give that template class such a deduction guide. You can see a sketch of this on Godbolt here.
However, making
a template class instead of a template alias would
introduce all sorts of other challenges, as it would become a distinct type
from
and functions taking a
would not take an
.
Perhaps we could come up with a solution, possibly involving conversions, that
would be acceptable.
3.2. Add dynamic_extents
And dynamic_mdspan
We could add some helper aliases or classes to simplify the use case of all dynamic extents:
template < size_t N , size_t ... Extents > struct __generate_dynamic_extents { using type = typename __generate_dynamic_extents < N - 1 , dynamic_extent , Extents ... >:: type ; }; template < size_t ... Extents > struct __generate_dynamic_extents < 0 , Extents ... > { using type = extents < Extents ... > ; }; template < size_t N > using dynamic_extents = typename __generate_dynamic_extents < N >:: type ; template < class ElementType , size_t Rank > using dynamic_mdspan = basic_mdspan < ElementType , dynamic_extents < Rank >> ;
You can see a sketch of this on Godbolt here.
3.3. Rename ( mdspan | extents )
to static_ ( extents | mdspan )
If we do add something like
and
, why not make
it the default and give them the good names of
and
?
All dynamic extents seems like the far more common use case.
We could rename the current
to
and the current
to
.
4. P.S. No ptrdiff_t
A final, partially related note: [P0009R10] must be updated to use
instead of
.
As much as I love signed types,
, which shipped in
in C++20, is defined as a
.
If
,
, and
use
, users will
experience all sorts of signedness and narrowing warnings when trying to use
with these facilities.