4405. mdspan constructor should disallow derived to base conversions

Section: 23.7.3.6.2 [mdspan.mdspan.cons] Status: New Submitter: Hewill Kang Opened: 2025-10-05 Last modified: 2025-10-10

Priority: Not Prioritized

View all issues with New status.

Discussion:

Unlike ranges::subrange or span, mdspan syntactically allows a multidimensional viewing base class via a derived class pointer (demo):

#include <span>
#include <ranges>
#include <mdspan>

struct Base {};
struct Derived : Base {};
std::array<Derived, 12> arr;
std::ranges::subrange<Base*> s(arr); // error, slicing
std::span<Base> sp(arr.data(), arr.size()); // error, slicing
std::mdspan<Base, std::dims<1>> msp(arr.data(), arr.size()); // ok

Given that we intend to reject object slicing for both default_accessor and aligned_accessor, there seems no reason not to reject this invalid pointer arithmetic for mdspan.

Proposed resolution:

This wording is relative to N5014.

[Drafting note: The exposition-only concept convertible-to-non-slicing comes from 25.5.4.1 [range.subrange.general].]

  1. Modify 23.7.3.6.1 [mdspan.mdspan.overview] as indicated:

    namespace std {
      template<class ElementType, class Extents, class LayoutPolicy = layout_right,
               class AccessorPolicy = default_accessor<ElementType>>
      class mdspan {
      public:
        using extents_type = Extents;
        using layout_type = LayoutPolicy;
        using accessor_type = AccessorPolicy;
        using mapping_type = typename layout_type::template mapping<extents_type>;
        using element_type = ElementType;
        using value_type = remove_cv_t<element_type>;
        using index_type = typename extents_type::index_type;
        using size_type = typename extents_type::size_type;
        using rank_type = typename extents_type::rank_type;
        using data_handle_type = typename accessor_type::data_handle_type;
        using reference = typename accessor_type::reference;
        […]
        template<class... OtherIndexTypes>
          constexpr explicit mdspan(convertible-to-non-slicing<data_handle_type> auto ptr, OtherIndexTypes... exts);
        template<class OtherIndexType, size_t N>
          constexpr explicit(N != rank_dynamic())
            mdspan(convertible-to-non-slicing<data_handle_type> auto p, span<OtherIndexType, N> exts);
        template<class OtherIndexType, size_t N>
          constexpr explicit(N != rank_dynamic())
            mdspan(convertible-to-non-slicing<data_handle_type> auto p, const array<OtherIndexType, N>& exts);
        constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const extents_type& ext);
        constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m);
        constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m, const accessor_type& a);
        […]
      };
      […]
    }
    
  2. Modify 23.7.3.6.2 [mdspan.mdspan.cons] as indicated:

    template<class... OtherIndexTypes>
      constexpr explicit mdspan(convertible-to-non-slicing<data_handle_type> auto p, OtherIndexTypes... exts);
    

    -4- Let N be sizeof...(OtherIndexTypes).

    -5- Constraints: […]

    […]

    template<class OtherIndexType, size_t N>
      constexpr explicit(N != rank_dynamic())
        mdspan(convertible-to-non-slicing<data_handle_type> auto p, span<OtherIndexType, N> exts);
    template<class OtherIndexType, size_t N>
      constexpr explicit(N != rank_dynamic())
        mdspan(convertible-to-non-slicing<data_handle_type> auto p, const array<OtherIndexType, N>& exts);
    

    -8- Constraints: […]

    […]

    constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const extents_type& ext);
    

    -11- Constraints: […]

    […]

    constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m);
    

    -14- Constraints: […]

    […]

    constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m, const accessor_type& a);
    

    -17- Preconditions: […]

    […]