1 | // Copyright (C) 2023 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QSPAN_H |
5 | #define QSPAN_H |
6 | |
7 | #include <QtCore/qcompilerdetection.h> |
8 | #include <QtCore/qtypes.h> |
9 | #include <QtCore/qcontainerfwd.h> |
10 | |
11 | #include <array> |
12 | #include <cstddef> |
13 | #include <cassert> |
14 | #include <initializer_list> |
15 | #include <QtCore/q20iterator.h> |
16 | #include <QtCore/q20memory.h> |
17 | #ifdef __cpp_lib_span |
18 | #include <span> |
19 | #endif |
20 | #include <QtCore/q20type_traits.h> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | // like std::dynamic_extent |
25 | namespace q20 { |
26 | inline constexpr auto dynamic_extent = std::size_t(-1); |
27 | } // namespace q20 |
28 | |
29 | QT_BEGIN_INCLUDE_NAMESPACE |
30 | #ifdef __cpp_lib_span |
31 | #ifdef __cpp_lib_concepts |
32 | namespace std::ranges { |
33 | // Officially, these are defined in <ranges>, but that is a heavy-hitter header. |
34 | // OTOH, <span> must specialize these variable templates, too, so we assume that |
35 | // <span> includes some meaningful subset of <ranges> and just go ahead and use them: |
36 | template <typename T, std::size_t E> |
37 | constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true; |
38 | template <typename T, std::size_t E> |
39 | constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true; |
40 | } // namespace std::ranges |
41 | #endif // __cpp_lib_concepts |
42 | #endif // __cpp_lib_span |
43 | QT_END_INCLUDE_NAMESPACE |
44 | |
45 | namespace QSpanPrivate { |
46 | |
47 | template <typename T, std::size_t E> class QSpanBase; |
48 | |
49 | template <typename T> |
50 | struct is_qspan_helper : std::false_type {}; |
51 | template <typename T, std::size_t E> |
52 | struct is_qspan_helper<QSpan<T, E>> : std::true_type {}; |
53 | template <typename T, std::size_t E> |
54 | struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {}; |
55 | template <typename T> |
56 | using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>; |
57 | |
58 | template <typename T> |
59 | struct is_std_span_helper : std::false_type {}; |
60 | #ifdef __cpp_lib_span |
61 | template <typename T, std::size_t E> |
62 | struct is_std_span_helper<std::span<T, E>> : std::true_type {}; |
63 | #endif // __cpp_lib_span |
64 | template <typename T> |
65 | using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>; |
66 | |
67 | template <typename T> |
68 | struct is_std_array_helper : std::false_type {}; |
69 | template <typename T, std::size_t N> |
70 | struct is_std_array_helper<std::array<T, N>> : std::true_type {}; |
71 | template <typename T> |
72 | using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>; |
73 | |
74 | template <typename From, typename To> |
75 | using is_qualification_conversion = |
76 | std::is_convertible<From(*)[], To(*)[]>; // https://eel.is/c++draft/span.cons#note-1 |
77 | template <typename From, typename To> |
78 | constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value; |
79 | |
80 | namespace AdlTester { |
81 | #define MAKE_ADL_TEST(what) \ |
82 | using std:: what; /* bring into scope */ \ |
83 | template <typename T> using what ## _result = decltype( what (std::declval<T&&>())); \ |
84 | /* end */ |
85 | MAKE_ADL_TEST(begin) |
86 | MAKE_ADL_TEST(data) |
87 | MAKE_ADL_TEST(size) |
88 | #undef MAKE_ADL_TEST |
89 | } |
90 | |
91 | // Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s, |
92 | // not doing the extra work C++20 requires |
93 | template <typename Range> |
94 | AdlTester::begin_result<Range> adl_begin(Range &&r) { using std::begin; return begin(r); } |
95 | template <typename Range> |
96 | AdlTester::data_result<Range> adl_data(Range &&r) { using std::data; return data(r); } |
97 | template <typename Range> |
98 | AdlTester::size_result<Range> adl_size(Range &&r) { using std::size; return size(r); } |
99 | |
100 | // Replacement for std::ranges::iterator_t (which depends on C++20 std::ranges::begin) |
101 | // This one uses adl_begin() instead. |
102 | template <typename Range> |
103 | using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>())); |
104 | template <typename Range> |
105 | using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>; |
106 | |
107 | template <typename T> |
108 | class QSpanCommon { |
109 | protected: |
110 | template <typename Iterator> |
111 | using is_compatible_iterator = std::conjunction< |
112 | // ### C++20: extend to contiguous_iteratorss |
113 | std::is_base_of< |
114 | std::random_access_iterator_tag, |
115 | typename std::iterator_traits<Iterator>::iterator_category |
116 | >, |
117 | is_qualification_conversion< |
118 | std::remove_reference_t<q20::iter_reference_t<Iterator>>, |
119 | T |
120 | > |
121 | >; |
122 | template <typename Iterator, typename End> |
123 | using is_compatible_iterator_and_sentinel = std::conjunction< |
124 | // ### C++20: extend to contiguous_iterators and real sentinels |
125 | is_compatible_iterator<Iterator>, |
126 | std::negation<std::is_convertible<End, std::size_t>> |
127 | >; |
128 | template <typename Range, typename = void> // wrap use of SFINAE-unfriendly iterator_t: |
129 | struct is_compatible_range_helper : std::false_type {}; |
130 | template <typename Range> |
131 | struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>> |
132 | : is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {}; |
133 | template <typename Range> |
134 | using is_compatible_range = std::conjunction< |
135 | // ### C++20: extend to contiguous_iterators |
136 | std::negation<is_qspan<Range>>, |
137 | std::negation<is_std_span<Range>>, |
138 | std::negation<is_std_array<Range>>, |
139 | std::negation<std::is_array<q20::remove_cvref_t<Range>>>, |
140 | is_compatible_range_helper<Range> |
141 | >; |
142 | |
143 | // constraints |
144 | template <typename Iterator> |
145 | using if_compatible_iterator = std::enable_if_t< |
146 | is_compatible_iterator<Iterator>::value |
147 | , bool>; |
148 | template <typename Iterator, typename End> |
149 | using if_compatible_iterator_and_sentinel = std::enable_if_t< |
150 | is_compatible_iterator_and_sentinel<Iterator, End>::value |
151 | , bool>; |
152 | template <typename Range> |
153 | using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>; |
154 | }; // class QSpanCommon |
155 | |
156 | template <typename T, std::size_t E> |
157 | class QSpanBase : protected QSpanCommon<T> |
158 | { |
159 | static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()}, |
160 | "QSpan only supports extents that fit into the signed size type (qsizetype)."); |
161 | |
162 | template <typename S, std::size_t N> |
163 | using if_compatible_array = std::enable_if_t< |
164 | N == E && is_qualification_conversion_v<S, T> |
165 | , bool>; |
166 | |
167 | template <typename S> |
168 | using if_qualification_conversion = std::enable_if_t< |
169 | is_qualification_conversion_v<S, T> |
170 | , bool>; |
171 | protected: |
172 | using Base = QSpanCommon<T>; |
173 | |
174 | // data members: |
175 | T *m_data; |
176 | static constexpr qsizetype m_size = qsizetype(E); |
177 | |
178 | // types and constants: |
179 | // (in QSpan only) |
180 | |
181 | // constructors (need to be public d/t the way ctor inheriting works): |
182 | public: |
183 | template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true> |
184 | Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {} |
185 | |
186 | template <typename It, typename Base::template if_compatible_iterator<It> = true> |
187 | explicit constexpr QSpanBase(It first, qsizetype count) |
188 | : m_data{q20::to_address(first)} |
189 | { |
190 | Q_ASSERT(count == m_size); |
191 | } |
192 | |
193 | template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true> |
194 | explicit constexpr QSpanBase(It first, End last) |
195 | : QSpanBase(first, last - first) {} |
196 | |
197 | template <size_t N, std::enable_if_t<N == E, bool> = true> |
198 | Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept |
199 | : QSpanBase(arr, N) {} |
200 | |
201 | template <typename S, size_t N, if_compatible_array<S, N> = true> |
202 | Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept |
203 | : QSpanBase(arr.data(), N) {} |
204 | |
205 | template <typename S, size_t N, if_compatible_array<S, N> = true> |
206 | Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept |
207 | : QSpanBase(arr.data(), N) {} |
208 | |
209 | template <typename Range, typename Base::template if_compatible_range<Range> = true> |
210 | Q_IMPLICIT constexpr QSpanBase(Range &&r) |
211 | : QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either) |
212 | qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>() |
213 | {} |
214 | |
215 | template <typename S, if_qualification_conversion<S> = true> |
216 | Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept |
217 | : QSpanBase(other.data(), other.size()) |
218 | {} |
219 | |
220 | template <typename S, if_qualification_conversion<S> = true> |
221 | Q_IMPLICIT constexpr QSpanBase(QSpan<S> other) |
222 | : QSpanBase(other.data(), other.size()) |
223 | {} |
224 | |
225 | template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true> |
226 | Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il) |
227 | : QSpanBase(il.begin(), il.size()) |
228 | {} |
229 | |
230 | #ifdef __cpp_lib_span |
231 | template <typename S, if_qualification_conversion<S> = true> |
232 | Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept |
233 | : QSpanBase(other.data(), other.size()) |
234 | {} |
235 | |
236 | template <typename S, if_qualification_conversion<S> = true> |
237 | Q_IMPLICIT constexpr QSpanBase(std::span<S> other) |
238 | : QSpanBase(other.data(), other.size()) |
239 | {} |
240 | #endif // __cpp_lib_span |
241 | }; // class QSpanBase (fixed extent) |
242 | |
243 | template <typename T> |
244 | class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T> |
245 | { |
246 | template <typename S> |
247 | using if_qualification_conversion = std::enable_if_t< |
248 | is_qualification_conversion_v<S, T> |
249 | , bool>; |
250 | protected: |
251 | using Base = QSpanCommon<T>; |
252 | |
253 | // data members: |
254 | T *m_data; |
255 | qsizetype m_size; |
256 | |
257 | // constructors (need to be public d/t the way ctor inheriting works): |
258 | public: |
259 | Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {} |
260 | |
261 | template <typename It, typename Base::template if_compatible_iterator<It> = true> |
262 | Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count) |
263 | : m_data{q20::to_address(first)}, m_size{count} {} |
264 | |
265 | template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true> |
266 | Q_IMPLICIT constexpr QSpanBase(It first, End last) |
267 | : QSpanBase(first, last - first) {} |
268 | |
269 | template <size_t N> |
270 | Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept |
271 | : QSpanBase(arr, N) {} |
272 | |
273 | template <typename S, size_t N, if_qualification_conversion<S> = true> |
274 | Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept |
275 | : QSpanBase(arr.data(), N) {} |
276 | |
277 | template <typename S, size_t N, if_qualification_conversion<S> = true> |
278 | Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept |
279 | : QSpanBase(arr.data(), N) {} |
280 | |
281 | template <typename Range, typename Base::template if_compatible_range<Range> = true> |
282 | Q_IMPLICIT constexpr QSpanBase(Range &&r) |
283 | : QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either) |
284 | qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>() |
285 | {} |
286 | |
287 | template <typename S, size_t N, if_qualification_conversion<S> = true> |
288 | Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept |
289 | : QSpanBase(other.data(), other.size()) |
290 | {} |
291 | |
292 | template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true> |
293 | Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il) noexcept |
294 | : QSpanBase(il.begin(), il.size()) |
295 | {} |
296 | |
297 | #ifdef __cpp_lib_span |
298 | template <typename S, size_t N, if_qualification_conversion<S> = true> |
299 | Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept |
300 | : QSpanBase(other.data(), other.size()) |
301 | {} |
302 | #endif // __cpp_lib_span |
303 | }; // class QSpanBase (dynamic extent) |
304 | |
305 | } // namespace QSpanPrivate |
306 | |
307 | template <typename T, std::size_t E> |
308 | class QSpan |
309 | #ifndef Q_QDOC |
310 | : private QSpanPrivate::QSpanBase<T, E> |
311 | #endif |
312 | { |
313 | using Base = QSpanPrivate::QSpanBase<T, E>; |
314 | Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0, |
315 | [[maybe_unused]] qsizetype n = 1) const |
316 | { |
317 | Q_ASSERT(pos >= 0); |
318 | Q_ASSERT(pos <= size()); |
319 | Q_ASSERT(n >= 0); |
320 | Q_ASSERT(n <= size() - pos); |
321 | } |
322 | |
323 | template <std::size_t N> |
324 | static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent; |
325 | public: |
326 | // constants and types |
327 | using value_type = std::remove_cv_t<T>; |
328 | #ifdef QT_COMPILER_HAS_LWG3346 |
329 | using iterator_concept = std::contiguous_iterator_tag; |
330 | using element_type = T; |
331 | #endif |
332 | using size_type = qsizetype; // difference to std::span |
333 | using difference_type = qptrdiff; // difference to std::span |
334 | using pointer = T*; |
335 | using const_pointer = const T*; |
336 | using reference = T&; |
337 | using const_reference = const T&; |
338 | using iterator = pointer; // implementation-defined choice |
339 | using const_iterator = const_pointer; // implementation-defined choice |
340 | using reverse_iterator = std::reverse_iterator<iterator>; |
341 | using const_reverse_iterator = std::reverse_iterator<const_iterator>; |
342 | static constexpr std::size_t extent = E; |
343 | |
344 | // [span.cons], constructors, copy, and assignment |
345 | using Base::Base; |
346 | #ifdef Q_QDOC |
347 | template <typename It> using if_compatible_iterator = bool; |
348 | template <typename S> using if_qualification_conversion = bool; |
349 | template <typename Range> using if_compatible_range = bool; |
350 | template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, qsizetype count); |
351 | template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, It last); |
352 | template <size_t N> constexpr QSpan(q20::type_identity_t<T> (&arr)[N]) noexcept; |
353 | template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::array<S, N> &arr) noexcept; |
354 | template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(const std::array<S, N> &arr) noexcept; |
355 | template <typename Range, if_compatible_range<Range> = true> constexpr QSpan(Range &&r); |
356 | template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(QSpan<S, N> other) noexcept; |
357 | template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::span<S, N> other) noexcept; |
358 | constexpr QSpan(std::initializer_list<value_type> il); |
359 | #endif // Q_QDOC |
360 | |
361 | // [span.obs] |
362 | [[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; } |
363 | [[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); } |
364 | [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } |
365 | |
366 | // [span.elem] |
367 | [[nodiscard]] constexpr reference operator[](size_type idx) const |
368 | { verify(pos: idx); return data()[idx]; } |
369 | [[nodiscard]] constexpr reference front() const { verify(); return *data(); } |
370 | [[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; } |
371 | [[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; } |
372 | |
373 | // [span.iterators] |
374 | [[nodiscard]] constexpr iterator begin() const noexcept { return data(); } |
375 | [[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); } |
376 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } |
377 | [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } |
378 | [[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } |
379 | [[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } |
380 | [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } |
381 | [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } |
382 | |
383 | // [span.sub] |
384 | template <std::size_t Count> |
385 | [[nodiscard]] constexpr QSpan<T, Count> first() const |
386 | noexcept(subspan_always_succeeds_v<Count>) |
387 | { |
388 | static_assert(Count <= E, |
389 | "Count cannot be larger than the span's extent."); |
390 | verify(pos: 0, n: Count); |
391 | return QSpan<T, Count>{data(), Count}; |
392 | } |
393 | |
394 | template <std::size_t Count> |
395 | [[nodiscard]] constexpr QSpan<T, Count> last() const |
396 | noexcept(subspan_always_succeeds_v<Count>) |
397 | { |
398 | static_assert(Count <= E, |
399 | "Count cannot be larger than the span's extent."); |
400 | verify(pos: 0, n: Count); |
401 | return QSpan<T, Count>{data() + (size() - Count), Count}; |
402 | } |
403 | |
404 | template <std::size_t Offset> |
405 | [[nodiscard]] constexpr auto subspan() const |
406 | noexcept(subspan_always_succeeds_v<Offset>) |
407 | { |
408 | static_assert(Offset <= E, |
409 | "Offset cannot be larger than the span's extent."); |
410 | verify(pos: Offset, n: 0); |
411 | if constexpr (E == q20::dynamic_extent) |
412 | return QSpan<T>{data() + Offset, qsizetype(size() - Offset)}; |
413 | else |
414 | return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)}; |
415 | } |
416 | |
417 | template <std::size_t Offset, std::size_t Count> |
418 | [[nodiscard]] constexpr auto subspan() const |
419 | noexcept(subspan_always_succeeds_v<Offset + Count>) |
420 | { return subspan<Offset>().template first<Count>(); } |
421 | |
422 | [[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(pos: 0, n); return {data(), n}; } |
423 | [[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(pos: 0, n); return {data() + (size() - n), n}; } |
424 | [[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, n: 0); return {data() + pos, size() - pos}; } |
425 | [[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); } |
426 | |
427 | // Qt-compatibility API: |
428 | [[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); } |
429 | // nullary first()/last() clash with first<>() and last<>(), so they're not provided for QSpan |
430 | [[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); } |
431 | [[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); } |
432 | |
433 | private: |
434 | // [span.objectrep] |
435 | [[nodiscard]] friend |
436 | QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)> |
437 | as_bytes(QSpan s) noexcept |
438 | { |
439 | using R = QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>; |
440 | return R{reinterpret_cast<const std::byte *>(s.data()), s.size_bytes()}; |
441 | } |
442 | |
443 | template <typename U> |
444 | using if_mutable = std::enable_if_t<!std::is_const_v<U>, bool>; |
445 | |
446 | #ifndef Q_QDOC |
447 | template <typename T2 = T, if_mutable<T2> = true> |
448 | #endif |
449 | [[nodiscard]] friend |
450 | QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)> |
451 | as_writable_bytes(QSpan s) noexcept |
452 | { |
453 | using R = QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>; |
454 | return R{reinterpret_cast<std::byte *>(s.data()), s.size_bytes()}; |
455 | } |
456 | }; // class QSpan |
457 | |
458 | // [span.deduct] |
459 | template <class It, class EndOrSize> |
460 | QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>; |
461 | template <class T, std::size_t N> |
462 | QSpan(T (&)[N]) -> QSpan<T, N>; |
463 | template <class T, std::size_t N> |
464 | QSpan(std::array<T, N> &) -> QSpan<T, N>; |
465 | template <class T, std::size_t N> |
466 | QSpan(const std::array<T, N> &) -> QSpan<const T, N>; |
467 | template <class R> |
468 | QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>; |
469 | |
470 | QT_END_NAMESPACE |
471 | |
472 | #endif // QSPAN_H |
473 |
Definitions
- dynamic_extent
- is_qspan_helper
- is_qspan_helper
- is_qspan_helper
- is_std_span_helper
- is_std_array_helper
- is_std_array_helper
- is_qualification_conversion_v
- adl_begin
- adl_data
- adl_size
- QSpanCommon
- is_compatible_range_helper
- is_compatible_range_helper
- QSpanBase
- m_size
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpanBase
- QSpan
- verify
- subspan_always_succeeds_v
- extent
- size
- size_bytes
- empty
- operator[]
- front
- back
- data
- begin
- end
- cbegin
- cend
- rbegin
- rend
- crbegin
- crend
- first
- last
- subspan
- subspan
- first
- last
- subspan
- subspan
- isEmpty
- sliced
- sliced
- as_bytes
Learn to use CMake with our Intro Training
Find out more