libstdc++: Better CTAD for span and mdspan [PR120914].

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.

Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
This commit is contained in:
Luc Grosheintz
2025-07-08 11:49:21 +02:00
committed by Tomasz Kamiński
parent bb6075e711
commit 9c600a7e6c
5 changed files with 69 additions and 7 deletions

View File

@@ -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>)

View File

@@ -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};
}
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 &&)

View File

@@ -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;
}

View File

@@ -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());

View File

@@ -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));
}