Skip to content

Commit 7cafe04

Browse files
author
Xiaoyang Liu
authored
[libc++] P3029R1: Better mdspan's CTAD (llvm#87873)
## Abstract This pull request implements [P3029R1](https://wg21.link/P3029R1). The paper discusses the current behavior of `mdspan`'s most common pointer-indices CTAD, where the `Extents` template parameter is deduced as `dextents` (dynamic extents), even when passing compile-time constant values. The author believes this behavior is suboptimal, as it doesn't take advantage of the compile-time information. The proposed change suggests deducing static extents if `integral_constant`-like constants are passed, resulting in more intuitive syntax and less error-prone code. ## Reference - [P3029R1](https://wg21.link/P3029R1) - [Draft C++ Standard: [span.syn]](https://eel.is/c++draft/span.syn) - [Draft C++ Standard: [mdspan.syn]](https://eel.is/c++draft/mdspan.syn)
1 parent 6a85cf8 commit 7cafe04

File tree

7 files changed

+84
-5
lines changed

7 files changed

+84
-5
lines changed

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Implemented Papers
4747
- P3142R0 - Printing Blank Lines with ``println`` (as DR against C++23)
4848
- P2302R4 - ``std::ranges::contains``
4949
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
50+
- P3029R1 - Better ``mdspan``'s CTAD
5051

5152
Improvements and New Features
5253
-----------------------------

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@
6161
"`P1068R11 <https://wg21.link/P1068R11>`__","LWG","Vector API for random number generation","Tokyo March 2024","","",""
6262
"`P2944R3 <https://wg21.link/P2944R3>`__","LWG","Comparisons for ``reference_wrapper``","Tokyo March 2024","","",""
6363
"`P2642R6 <https://wg21.link/P2642R6>`__","LWG","Padded ``mdspan`` layouts","Tokyo March 2024","","",""
64-
"`P3029R1 <https://wg21.link/P3029R1>`__","LWG","Better ``mdspan``'s CTAD","Tokyo March 2024","","",""
64+
"`P3029R1 <https://wg21.link/P3029R1>`__","LWG","Better ``mdspan``'s CTAD","Tokyo March 2024","|Complete|","19.0",""
6565
"","","","","","",""

libcxx/include/__mdspan/mdspan.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,17 @@ class mdspan {
266266
friend class mdspan;
267267
};
268268

269+
# if _LIBCPP_STD_VER >= 26
270+
template <class _ElementType, class... _OtherIndexTypes>
271+
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
272+
explicit mdspan(_ElementType*,
273+
_OtherIndexTypes...) -> mdspan<_ElementType, extents<size_t, __maybe_static_ext<_OtherIndexTypes>...>>;
274+
# else
269275
template <class _ElementType, class... _OtherIndexTypes>
270276
requires((is_convertible_v<_OtherIndexTypes, size_t> && ...) && (sizeof...(_OtherIndexTypes) > 0))
271277
explicit mdspan(_ElementType*, _OtherIndexTypes...)
272278
-> mdspan<_ElementType, dextents<size_t, sizeof...(_OtherIndexTypes)>>;
279+
# endif
273280

274281
template <class _Pointer>
275282
requires(is_pointer_v<remove_reference_t<_Pointer>>)

libcxx/include/mdspan

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,11 @@ namespace std {
370370
template<class ElementType, class... Integrals>
371371
requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
372372
explicit mdspan(ElementType*, Integrals...)
373-
-> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;
373+
-> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>; // until C++26
374+
template<class ElementType, class... Integrals>
375+
requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
376+
explicit mdspan(ElementType*, Integrals...)
377+
-> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>; // since C++26
374378
375379
template<class ElementType, class OtherIndexType, size_t N>
376380
mdspan(ElementType*, span<OtherIndexType, N>)

libcxx/include/span

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@ namespace std {
1818
// constants
1919
inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();
2020
21+
template<class T>
22+
concept integral-constant-like = // exposition only, since C++26
23+
is_integral_v<decltype(T::value)> &&
24+
!is_same_v<bool, remove_const_t<decltype(T::value)>> &&
25+
convertible_to<T, decltype(T::value)> &&
26+
equality_comparable_with<T, decltype(T::value)> &&
27+
bool_constant<T() == T::value>::value &&
28+
bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
29+
30+
template<class T>
31+
constexpr size_t maybe-static-ext = dynamic_extent; // exposition only, since C++26
32+
template<integral-constant-like T>
33+
constexpr size_t maybe-static-ext<T> = {T::value};
34+
2135
// [views.span], class template span
2236
template <class ElementType, size_t Extent = dynamic_extent>
2337
class span;
@@ -110,7 +124,9 @@ private:
110124
};
111125
112126
template<class It, class EndOrSize>
113-
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
127+
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>; // until C++26
128+
template<class It, class EndOrSize>
129+
span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<It>>, maybe-static-ext<EndOrSize>>; // since C++26
114130
115131
template<class T, size_t N>
116132
span(T (&)[N]) -> span<T, N>;
@@ -129,6 +145,8 @@ template<class R>
129145
*/
130146

131147
#include <__assert>
148+
#include <__concepts/convertible_to.h>
149+
#include <__concepts/equality_comparable.h>
132150
#include <__config>
133151
#include <__fwd/array.h>
134152
#include <__fwd/span.h>
@@ -143,15 +161,19 @@ template<class R>
143161
#include <__ranges/enable_borrowed_range.h>
144162
#include <__ranges/enable_view.h>
145163
#include <__ranges/size.h>
164+
#include <__type_traits/integral_constant.h>
146165
#include <__type_traits/is_array.h>
147166
#include <__type_traits/is_const.h>
148167
#include <__type_traits/is_convertible.h>
168+
#include <__type_traits/is_integral.h>
169+
#include <__type_traits/is_same.h>
170+
#include <__type_traits/remove_const.h>
149171
#include <__type_traits/remove_cv.h>
150172
#include <__type_traits/remove_cvref.h>
151173
#include <__type_traits/remove_reference.h>
152174
#include <__type_traits/type_identity.h>
153175
#include <__utility/forward.h>
154-
#include <cstddef> // for byte
176+
#include <cstddef> // for byte
155177
#include <initializer_list>
156178
#include <stdexcept>
157179
#include <version>
@@ -563,8 +585,26 @@ _LIBCPP_HIDE_FROM_ABI auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept {
563585
return __s.__as_writable_bytes();
564586
}
565587

588+
# if _LIBCPP_STD_VER >= 26
589+
template <class _Tp>
590+
concept __integral_constant_like =
591+
is_integral_v<decltype(_Tp::value)> && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>> &&
592+
convertible_to<_Tp, decltype(_Tp::value)> && equality_comparable_with<_Tp, decltype(_Tp::value)> &&
593+
bool_constant<_Tp() == _Tp::value>::value &&
594+
bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) == _Tp::value>::value;
595+
596+
template <class _Tp>
597+
inline constexpr size_t __maybe_static_ext = dynamic_extent;
598+
599+
template <__integral_constant_like _Tp>
600+
inline constexpr size_t __maybe_static_ext<_Tp> = {_Tp::value};
601+
602+
template <contiguous_iterator _It, class _EndOrSize>
603+
span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;
604+
# else
566605
template <contiguous_iterator _It, class _EndOrSize>
567606
span(_It, _EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;
607+
# endif
568608

569609
template <class _Tp, size_t _Sz>
570610
span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;

libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@
2222
// template<class ElementType, class... Integrals>
2323
// requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
2424
// explicit mdspan(ElementType*, Integrals...)
25-
// -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>;
25+
// -> mdspan<ElementType, dextents<size_t, sizeof...(Integrals)>>; // until C++26
26+
// template<class ElementType, class... Integrals>
27+
// requires((is_convertible_v<Integrals, size_t> && ...) && sizeof...(Integrals) > 0)
28+
// explicit mdspan(ElementType*, Integrals...)
29+
// -> mdspan<ElementType, extents<size_t, maybe-static-ext<Integrals>...>>; // since C++26
2630
//
2731
// template<class ElementType, class OtherIndexType, size_t N>
2832
// mdspan(ElementType*, span<OtherIndexType, N>)
@@ -102,6 +106,18 @@ constexpr bool test_no_layout_deduction_guides(const H& handle, const A&) {
102106
// deduction from pointer and integral like
103107
ASSERT_SAME_TYPE(decltype(std::mdspan(handle, 5, SizeTIntType(6))), std::mdspan<T, std::dextents<size_t, 2>>);
104108

109+
#if _LIBCPP_STD_VER >= 26
110+
// P3029R1: deduction from `integral_constant`
111+
ASSERT_SAME_TYPE(
112+
decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{})), std::mdspan<T, std::extents<size_t, 5>>);
113+
ASSERT_SAME_TYPE(decltype(std::mdspan(handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent)),
114+
std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent>>);
115+
ASSERT_SAME_TYPE(
116+
decltype(std::mdspan(
117+
handle, std::integral_constant<size_t, 5>{}, std::dynamic_extent, std::integral_constant<size_t, 7>{})),
118+
std::mdspan<T, std::extents<size_t, 5, std::dynamic_extent, 7>>);
119+
#endif
120+
105121
std::array<char, 3> exts;
106122
// deduction from pointer and array
107123
ASSERT_SAME_TYPE(decltype(std::mdspan(handle, exts)), std::mdspan<T, std::dextents<size_t, 3>>);

libcxx/test/std/containers/views/views.span/span.cons/deduct.pass.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <iterator>
3232
#include <memory>
3333
#include <string>
34+
#include <type_traits>
3435

3536
#include "test_macros.h"
3637

@@ -48,6 +49,16 @@ void test_iterator_sentinel() {
4849
assert(s.size() == std::size(arr));
4950
assert(s.data() == std::data(arr));
5051
}
52+
53+
#if _LIBCPP_STD_VER >= 26
54+
// P3029R1: deduction from `integral_constant`
55+
{
56+
std::span s{std::begin(arr), std::integral_constant<size_t, 3>{}};
57+
ASSERT_SAME_TYPE(decltype(s), std::span<int, 3>);
58+
assert(s.size() == std::size(arr));
59+
assert(s.data() == std::data(arr));
60+
}
61+
#endif
5162
}
5263

5364
void test_c_array() {

0 commit comments

Comments
 (0)