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.