[PATCH v2 3/3] libstdc++: Adjust span/mdspan CTAD for P2781R9.

Luc Grosheintz luc.grosheintz@gmail.com
Tue Sep 2 11:28:21 GMT 2025



On 9/2/25 10:21, Tomasz Kaminski wrote:
> On Tue, Sep 2, 2025 at 10:15 AM Luc Grosheintz <luc.grosheintz@gmail.com>
> wrote:
> 
>> A usecase for P2781R9 is more ergonomic creation of span and mdspan with
>> mixed static and dynamic extents, e.g.:
>>
>>      span(ptr, cw<3>)
>>      extents(cw<3>, 5, cw<7>)
>>      mdspan(ptr, cw<3>, 5, cw<7>)
>>
>> should be deduced as:
>>      span<..., 3>
>>      extents<..., 3, dyn, 7>
>>      mdspan<..., extents<..., 3, dyn, 7>>
>>
>> The change required is to strip cv-qualifiers and references from
>> `_Tp::value`, because of:
>>
>>      template<_CwFixedValue _X, typename>
>>        struct constant_wrapper : _CwOperators
>>        {
>>          static constexpr const auto& value = _X._M_data;
>>
>> libstdc++-v3/ChangeLog:
>>
>>          * include/std/span (__integral_constant_like): Allow the member
>>          `value` of a constant wrapping type to be a const reference of
>>          an integer.
>>          * testsuite/23_containers/mdspan/extents/misc.cc: Add test for
>>          cw and constant_wrapper.
>>          * testsuite/23_containers/mdspan/mdspan.cc: Ditto.
>>          * testsuite/23_containers/span/deduction.cc: Ditto.
>>
>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>> ---
>>   libstdc++-v3/include/std/span                 |  3 ++-
>>   .../23_containers/mdspan/extents/misc.cc      | 20 +++++++++++++------
>>   .../testsuite/23_containers/mdspan/mdspan.cc  | 20 ++++++++++++-------
>>   .../testsuite/23_containers/span/deduction.cc | 10 ++++++++++
>>   4 files changed, 39 insertions(+), 14 deletions(-)
>>
>> diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span
>> index 44f9b36a7ef..f9aa3c77e8e 100644
>> --- a/libstdc++-v3/include/std/span
>> +++ b/libstdc++-v3/include/std/span
>> @@ -480,7 +480,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>     namespace __detail
>>     {
>>       template<typename _Tp>
>> -      concept __integral_constant_like =
>> is_integral_v<decltype(_Tp::value)>
>> +      concept __integral_constant_like =
>> +       is_integral_v<remove_cvref_t<decltype(_Tp::value)>>
>>          && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>>
>>
> On the last LWG telecon  we have noticed, that this was not updated to
> remove_cvref_t above,
> and in consequence I believe, that:
> span(ptr, true_type{}); // does not work, it is not integral constant like
> span(ptr, cw<true>); // works,
> 
> Could you add test like above, and let me know what are the result, so I
> can report back?

Let's look at:

   std::span s16(a.data(), std::true_type{});
   std::span s17(a.data(), std::cw<true>);
   std::span s18(a.data(), true);

Quick reminder that `true_type` and `constant_wrapper` implicitly
convert to the underlying type, i.e. to bool.

1. Prior to any changes to the CTADs, s16 and s18, both compile and result
in dynamically sized spans:

https://godbolt.org/z/qeos99aMh

2. As per current wording of the standard and as implemented in v2 of this patch:

    - s16 has dynamic extent.
    - s17 has static extent `1`.
    - s18 has dynamic extent.

3. As per your suggestion:

     template<typename _Tp>
       concept __integral_constant_like =
	is_integral_v<remove_cvref_t<decltype(_Tp::value)>>
	&& !is_same_v<bool, remove_cvref_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;

     All three cases are deduced as having a dynamic extent.

Could you please expand a little on what "works" means? Because the code
seems to quite explicitly not consider "wrapped booleans" as integer
constants, i.e.

	&& !is_same_v<bool, remove_cvref_t<decltype(_Tp::value)>>

What's not clear to is if this is intentional, or if it's an attempt at
rejecting unrelated traits:

    template<typename T>
    class is_nice
    {
      constexpr static bool value = true;
    };

(There's many `std::is_*` that have a static member `value` and I wouldn't
consider those "integer constants".)

Let me know if you need anything else.

> Do not send an v3 yet, until I will finish review.
> 
>>          && convertible_to<_Tp, decltype(_Tp::value)>
>>          && equality_comparable_with<_Tp, decltype(_Tp::value)>
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>> index 8a43a682004..018c4f95930 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc
>> @@ -98,7 +98,7 @@ test_deduction(Extents... exts)
>>   }
>>
>>   constexpr bool
>> -test_integral_constant_deduction()
>> +test_deduction_from_constant()
>>   {
>>     auto verify = [](auto actual, auto expected)
>>       {
>> @@ -106,13 +106,21 @@ test_integral_constant_deduction()
>>         VERIFY(actual == expected);
>>       };
>>
>> -  constexpr auto c1 = std::integral_constant<size_t, 1>{};
>> -  constexpr auto c2 = std::integral_constant<int, 2>{};
>> +  constexpr auto i1 = std::integral_constant<size_t, 1>{};
>> +  constexpr auto i2 = 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(i1), std::extents<size_t, 1>{});
>> +  verify(std::extents(i2), std::extents<size_t, 2>{});
>> +  verify(std::extents(i1, 2), std::extents<size_t, 1, dyn>{2});
>> +
>> +#if __glibcxx_constant_wrapper
>> +  constexpr auto c2 = std::constant_wrapper<2>{};
>>     verify(std::extents(c2), std::extents<size_t, 2>{});
>> -  verify(std::extents(c1, 2), std::extents<size_t, 1, dyn>{2});
>> +  verify(std::extents(1, c2), std::extents<size_t, dyn, 2>{1});
>> +  verify(std::extents(std::cw<2>), std::extents<size_t, 2>{});
>> +  verify(std::extents(1, std::cw<2>), std::extents<size_t, dyn, 2>{1});
>> +#endif
>>     return true;
>>   }
>>
>> @@ -123,7 +131,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();
>> +  test_deduction_from_constant();
>>     return true;
>>   }
>>
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> index bdfc6ebcf56..2583047413c 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc
>> @@ -280,7 +280,7 @@ test_from_pointer_and_shape()
>>   }
>>
>>   constexpr bool
>> -test_from_pointer_and_integral_constant()
>> +test_from_pointer_and_constant()
>>   {
>>     std::array<double, 6> buffer{};
>>     double * ptr = buffer.data();
>> @@ -292,12 +292,18 @@ test_from_pointer_and_integral_constant()
>>         VERIFY(actual.extents() == expected.extents());
>>       };
>>
>> -  auto c3 = std::integral_constant<int, 3>{};
>> -  auto c6 = std::integral_constant<int, 6>{};
>> +  auto i3 = std::integral_constant<int, 3>{};
>> +  auto i6 = 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));
>> +  verify(std::mdspan(ptr, i6), std::extents(i6));
>> +  verify(std::mdspan(ptr, 2, i3), std::extents(2, i3));
>> +
>> +#if __glibcxx_constant_wrapper
>> +  auto c3 = std::constant_wrapper<3>{};
>> +  verify(std::mdspan(ptr, 2, c3), std::extents(2, i3));
>> +  verify(std::mdspan(ptr, 2, std::cw<3>), std::extents(2, i3));
>> +#endif
>>     return true;
>>   }
>>
>> @@ -729,8 +735,8 @@ 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_pointer_and_constant();
>> +  static_assert(test_from_pointer_and_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 c66db90222e..0d045505bed 100644
>> --- a/libstdc++-v3/testsuite/23_containers/span/deduction.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/span/deduction.cc
>> @@ -83,4 +83,14 @@ test01()
>>
>>     std::span s13(a.data(), std::integral_constant<size_t, 3>{});
>>     static_assert( is_static_span<long, 3>(s13));
>> +
>> +#if __glibcxx_constant_wrapper
>> +  auto c5 = std::constant_wrapper<5>{};
>> +
>> +  std::span s14(a.data(), std::cw<4>);
>> +  static_assert( is_static_span<long, 4>(s14));
>> +
>> +  std::span s15(a.data(), c5);
>> +  static_assert( is_static_span<long, 5>(s15));
>> +#endif
>>   }
>> --
>> 2.51.0
>>
>>
> 



More information about the Libstdc++ mailing list