This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

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: […]

    […]