[PATCH v2 2/2] libstdc++: Better CTAD for span and mdspan [PR120914].

Luc Grosheintz luc.grosheintz@gmail.com
Tue Jul 8 16:18:32 GMT 2025



On 7/8/25 17:11, Tomasz Kaminski wrote:
> This was merged. Thanks for working on it.
> 
> However, the constant_wrapper paper accepted in Sofia, modifies the concept:
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2781r8.html

Interesting! I'll go hiking for 3 days, then I'll look at this and
write the test related to negative numbers.

Are you proposing implementing the whole of P2781 or just the mdspan
related change?

> 
> On Tue, Jul 8, 2025 at 1:01 PM Tomasz Kaminski <tkaminsk@redhat.com> wrote:
> 
>> Maybe adding a negative test for the error when converting negative values
>> would be valuable then.
>> I think we can add it as separate commit later, I will try to get
>> everything merged today,
>>
>> On Tue, Jul 8, 2025 at 12:56 PM Jonathan Wakely <jwakely@redhat.com>
>> wrote:
>>
>>> On Tue, 8 Jul 2025 at 11:46, Tomasz Kaminski <tkaminsk@redhat.com> wrote:
>>>>
>>>>
>>>>
>>>> On Tue, Jul 8, 2025 at 12:08 PM Jonathan Wakely <jwakely@redhat.com>
>>> wrote:
>>>>>
>>>>> On Tue, 8 Jul 2025 at 10:54, Luc Grosheintz <luc.grosheintz@gmail.com>
>>> wrote:
>>>>>>
>>>>>> This implements P3029R1. In P3029R1, the CTAD for span is refined to
>>>>>> permit deducing the extent of the span from an integral constant,
>>> e.g.
>>>>>>
>>>>>>    span((T*) ptr, integral_constant<size_t, 5>{});
>>>>>>
>>>>>> is deduced as span<T, 5>. Similarly, in
>>>>>>
>>>>>>    auto exts = extents(integral_constant<int, 2>);
>>>>>>    auto md = mdspan((T*) ptr, integral_constant<int, 2>);
>>>>>>
>>>>>> exts and md have types extents<size_t, 2> and mdspan<double,
>>>>>> extents<size_t, 2>>, respectively.
>>>>>>
>>>>>>          PR libstdc++/120914
>>>>>>
>>>>>> libstdc++-v3/ChangeLog:
>>>>>>
>>>>>>          * include/std/span (span): Update CTAD to enable
>>>>>>          integral constants [P3029R1].
>>>>>>          * include/std/mdspan (extents): ditto.
>>>>>>          (mdspan): ditto.
>>>>>>          * testsuite/23_containers/span/deduction.cc: Test deduction
>>>>>>          guide.
>>>>>>          * testsuite/23_containers/mdspan/extents/misc.cc: ditto.
>>>>>>          * testsuite/23_containers/mdspan/mdspan.cc: ditto.
>>>>>>
>>>>>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>>>>>> ---
>>>>>>   libstdc++-v3/include/std/mdspan               |  8 ++----
>>>>>>   libstdc++-v3/include/std/span                 | 20 ++++++++++++++-
>>>>>>   .../23_containers/mdspan/extents/misc.cc      | 20 +++++++++++++++
>>>>>>   .../testsuite/23_containers/mdspan/mdspan.cc  | 25
>>> +++++++++++++++++++
>>>>>>   .../testsuite/23_containers/span/deduction.cc |  3 +++
>>>>>>   5 files changed, 69 insertions(+), 7 deletions(-)
>>>>>>
>>>>>> diff --git a/libstdc++-v3/include/std/mdspan
>>> b/libstdc++-v3/include/std/mdspan
>>>>>> index 0fd78570b3e..152dcb3e92a 100644
>>>>>> --- a/libstdc++-v3/include/std/mdspan
>>>>>> +++ b/libstdc++-v3/include/std/mdspan
>>>>>> @@ -406,10 +406,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>>>       template<typename _IndexType, size_t... _Counts>
>>>>>>         auto __build_dextents_type(integer_sequence<size_t,
>>> _Counts...>)
>>>>>>          -> extents<_IndexType, ((void) _Counts, dynamic_extent)...>;
>>>>>> -
>>>>>> -    template<typename _Tp>
>>>>>> -      consteval size_t
>>>>>> -      __dynamic_extent() { return dynamic_extent; }
>>>>>>     }
>>>>>>
>>>>>>     template<typename _IndexType, size_t _Rank>
>>>>>> @@ -419,7 +415,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>>>     template<typename... _Integrals>
>>>>>>       requires (is_convertible_v<_Integrals, size_t> && ...)
>>>>>>       explicit extents(_Integrals...) ->
>>>>>> -      extents<size_t, __mdspan::__dynamic_extent<_Integrals>()...>;
>>>>>> +      extents<size_t, __detail::__maybe_static_ext<_Integrals>...>;
>>>>>>
>>>>>>     struct layout_left
>>>>>>     {
>>>>>> @@ -1316,7 +1312,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>>>                && (sizeof...(_Integrals) > 0)
>>>>>>       explicit mdspan(_ElementType*, _Integrals...)
>>>>>>       -> mdspan<_ElementType,
>>>>>> -             extents<size_t,
>>> __mdspan::__dynamic_extent<_Integrals>()...>>;
>>>>>> +             extents<size_t,
>>> __detail::__maybe_static_ext<_Integrals>...>>;
>>>>>>
>>>>>>     template<typename _ElementType, typename _OIndexType, size_t _Nm>
>>>>>>       mdspan(_ElementType*, span<_OIndexType, _Nm>)
>>>>>> diff --git a/libstdc++-v3/include/std/span
>>> b/libstdc++-v3/include/std/span
>>>>>> index 49ab9109d83..5629a71b9bd 100644
>>>>>> --- a/libstdc++-v3/include/std/span
>>>>>> +++ b/libstdc++-v3/include/std/span
>>>>>> @@ -476,6 +476,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>>>       };
>>>>>>
>>>>>>     // deduction guides
>>>>>> +  namespace __detail
>>>>>> +  {
>>>>>> +    template<typename _Tp>
>>>>>> +      concept __integral_constant_like =
>>> is_integral_v<decltype(_Tp::value)>
>>>>>> +       && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>>
>>>>>> +       && convertible_to<_Tp, decltype(_Tp::value)>
>>>>>> +       && equality_comparable_with<_Tp, decltype(_Tp::value)>
>>>>>> +       && bool_constant<_Tp() == _Tp::value>::value
>>>>>> +       && bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) ==
>>> _Tp::value>
>>>>>> +            ::value;
>>>>>> +
>>>>>> +    template<typename _Tp>
>>>>>> +      constexpr size_t __maybe_static_ext = dynamic_extent;
>>>>>> +
>>>>>> +    template<__integral_constant_like _Tp>
>>>>>> +      constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
>>>>>
>>>>> Are the braces here to detect narrowing conversions?
>>>>>
>>>>> (The paper doesn't mention why they're used, as far as I can see)
>>>>
>>>> The information is in revision history (a bit hidden):
>>>> Initialized the maybe-static-ext's specialization with {T::value} to
>>> prevent conversions from negative values.
>>>
>>> Thanks. OK for trunk then.
>>>
>>>
>>>>>
>>>>>
>>>>>
>>>>>> +  }
>>>>>>
>>>>>>     template<typename _Type, size_t _ArrayExtent>
>>>>>>       span(_Type(&)[_ArrayExtent]) -> span<_Type, _ArrayExtent>;
>>>>>> @@ -489,7 +506,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>>>>
>>>>>>     template<contiguous_iterator _Iter, typename _End>
>>>>>>       span(_Iter, _End)
>>>>>> -      -> span<remove_reference_t<iter_reference_t<_Iter>>>;
>>>>>> +      -> span<remove_reference_t<iter_reference_t<_Iter>>,
>>>>>> +                                __detail::__maybe_static_ext<_End>>;
>>>>>>
>>>>>>     template<ranges::contiguous_range _Range>
>>>>>>       span(_Range &&)
>>>>>> diff --git
>>> a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>>> b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>>>>>> index e71fdc54230..bca8901685d 100644
>>>>>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>>>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>>>>>> @@ -97,6 +97,25 @@ test_deduction(Extents... exts)
>>>>>>     VERIFY(e == expected);
>>>>>>   }
>>>>>>
>>>>>> +constexpr bool
>>>>>> +test_integral_constant_deduction()
>>>>>> +{
>>>>>> +  auto verify = [](auto actual, auto expected)
>>>>>> +    {
>>>>>> +      static_assert(std::same_as<decltype(actual),
>>> decltype(expected)>);
>>>>>> +      VERIFY(actual == expected);
>>>>>> +    };
>>>>>> +
>>>>>> +  constexpr auto c1 = std::integral_constant<size_t, 1>{};
>>>>>> +  constexpr auto c2 = std::integral_constant<int, 2>{};
>>>>>> +
>>>>>> +  verify(std::extents(1), std::extents<size_t, dyn>{1});
>>>>>> +  verify(std::extents(c1), std::extents<size_t, 1>{});
>>>>>> +  verify(std::extents(c2), std::extents<size_t, 2>{});
>>>>>> +  verify(std::extents(c1, 2), std::extents<size_t, 1, dyn>{2});
>>>>>> +  return true;
>>>>>> +}
>>>>>> +
>>>>>>   constexpr bool
>>>>>>   test_deduction_all()
>>>>>>   {
>>>>>> @@ -104,6 +123,7 @@ test_deduction_all()
>>>>>>     test_deduction<1>(1);
>>>>>>     test_deduction<2>(1.0, 2.0f);
>>>>>>     test_deduction<3>(int(1), short(2), size_t(3));
>>>>>> +  test_integral_constant_deduction();
>>>>>>     return true;
>>>>>>   }
>>>>>>
>>>>>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>>> b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>>>>>> index 9252273bf66..a650fb19bdf 100644
>>>>>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>>>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>>>>>> @@ -245,6 +245,28 @@ test_from_pointer_and_shape()
>>>>>>     return true;
>>>>>>   }
>>>>>>
>>>>>> +constexpr bool
>>>>>> +test_from_pointer_and_integral_constant()
>>>>>> +{
>>>>>> +  std::array<double, 6> buffer{};
>>>>>> +  double * ptr = buffer.data();
>>>>>> +
>>>>>> +  auto verify = [ptr](auto actual, auto exts)
>>>>>> +    {
>>>>>> +      auto expected = std::mdspan<double, decltype(exts)>(ptr,
>>> exts);
>>>>>> +      static_assert(std::same_as<decltype(actual),
>>> decltype(expected)>);
>>>>>> +      VERIFY(actual.extents() == expected.extents());
>>>>>> +    };
>>>>>> +
>>>>>> +  auto c3 = std::integral_constant<int, 3>{};
>>>>>> +  auto c6 = std::integral_constant<int, 6>{};
>>>>>> +
>>>>>> +  verify(std::mdspan(ptr, 6), std::extents(6));
>>>>>> +  verify(std::mdspan(ptr, c6), std::extents(c6));
>>>>>> +  verify(std::mdspan(ptr, 2, c3), std::extents(2, c3));
>>>>>> +  return true;
>>>>>> +}
>>>>>> +
>>>>>>   constexpr bool
>>>>>>   test_from_extents()
>>>>>>   {
>>>>>> @@ -616,6 +638,9 @@ main()
>>>>>>     test_from_pointer_and_shape();
>>>>>>     static_assert(test_from_pointer_and_shape());
>>>>>>
>>>>>> +  test_from_pointer_and_integral_constant();
>>>>>> +  static_assert(test_from_pointer_and_integral_constant());
>>>>>> +
>>>>>>     test_from_extents();
>>>>>>     static_assert(test_from_extents());
>>>>>>
>>>>>> diff --git a/libstdc++-v3/testsuite/23_containers/span/deduction.cc
>>> b/libstdc++-v3/testsuite/23_containers/span/deduction.cc
>>>>>> index dce6cedf89b..c66db90222e 100644
>>>>>> --- a/libstdc++-v3/testsuite/23_containers/span/deduction.cc
>>>>>> +++ b/libstdc++-v3/testsuite/23_containers/span/deduction.cc
>>>>>> @@ -80,4 +80,7 @@ test01()
>>>>>>
>>>>>>     std::span s12(const_cast<const std::span<int>&>(s5));
>>>>>>     static_assert( is_dynamic_span<int>(s12) );
>>>>>> +
>>>>>> +  std::span s13(a.data(), std::integral_constant<size_t, 3>{});
>>>>>> +  static_assert( is_static_span<long, 3>(s13));
>>>>>>   }
>>>>>> --
>>>>>> 2.49.0
>>>>>>
>>>>>
>>>
>>>
> 



More information about the Libstdc++ mailing list