Vc 1.4.5
SIMD Vector Classes for C++
 
Loading...
Searching...
No Matches
span.h
1// -*- C++ -*-
2//===------------------------------ span ---------------------------------===//
3//
4// The LLVM Compiler Infrastructure
5//
6// This file is dual licensed under the MIT and the University of Illinois Open
7// Source Licenses. See LICENSE.TXT for details.
8//
9// Adapted for use with Vc:
10// Copyright © 2018 Matthias Kretz <kretz@kde.org>
11//===---------------------------------------------------------------------===//
12
13#ifndef VC_COMMON_SPAN_H_
14#define VC_COMMON_SPAN_H_
15
16#include <array> // for array
17#include <cstddef> // for ptrdiff_t
18#include <cstddef> // for std::byte
19#include <iterator> // for iterators
20#include <type_traits> // for remove_cv, etc
21#include "subscript.h" // for AdaptSubscriptOperator
22
23namespace Vc_VERSIONED_NAMESPACE
24{
25#ifdef __cpp_inline_variables
26inline
27#endif
28 constexpr ptrdiff_t dynamic_extent = -1;
29namespace Common
30{
31template <typename T, ptrdiff_t Extent = dynamic_extent> class span;
32
33template <typename T, ptrdiff_t Extent>
34constexpr auto begin(const span<T, Extent>& s) noexcept -> decltype(s.begin())
35{
36 return s.begin();
37}
38template <typename T, ptrdiff_t Extent>
39constexpr auto end(const span<T, Extent>& s) noexcept -> decltype(s.end())
40{
41 return s.end();
42}
43
44template <class T> struct _is_span_impl : public std::false_type {
45};
46
47template <class T, ptrdiff_t Extent>
48struct _is_span_impl<span<T, Extent>> : public std::true_type {
49};
50
51template <class T>
52struct _is_span : public _is_span_impl<typename std::remove_cv<T>::type> {
53};
54
55template <class T> struct _is_std_array_impl : public std::false_type {
56};
57
58template <class T, size_t Sz>
59struct _is_std_array_impl<array<T, Sz>> : public std::true_type {
60};
61
62template <class T>
63struct _is_std_array : public _is_std_array_impl<typename std::remove_cv<T>::type> {
64};
65
66template <class T, class ElementType, class = void>
67struct _is_span_compatible_container : public std::false_type {
68};
69
70template <class... Ts> using _void_t = void;
71
72template <class C> constexpr auto _std_data(C& c) -> decltype(c.data())
73{
74 return c.data();
75}
76template <class C> constexpr auto _std_data(const C& c) -> decltype(c.data())
77{
78 return c.data();
79}
80template <class T, std::size_t N> constexpr T* _std_data(T (&array)[N]) noexcept
81{
82 return array;
83}
84template <class E> constexpr const E* _std_data(std::initializer_list<E> il) noexcept
85{
86 return il.begin();
87}
88
89template <class C> constexpr auto _std_size(const C& c) -> decltype(c.size())
90{
91 return c.size();
92}
93template <class T, std::size_t N>
94constexpr std::size_t _std_size(const T (&array)[N]) noexcept
95{
96 return N;
97}
98
99template <class T, class ElementType>
100struct _is_span_compatible_container<
101 T, ElementType,
102 _void_t<
103 // is not a specialization of span
104 typename std::enable_if<!_is_span<T>::value, std::nullptr_t>::type,
105 // is not a specialization of array
106 typename std::enable_if<!_is_std_array<T>::value, std::nullptr_t>::type,
107 // is_array_v<Container> is false,
108 typename std::enable_if<!std::is_array<T>::value, std::nullptr_t>::type,
109 // data(cont) and size(cont) are well formed
110 decltype(data(std::declval<T>())), decltype(size(std::declval<T>())),
111 // remove_pointer_t<decltype(data(cont))>(*)[] is convertible to ElementType(*)[]
112 typename std::enable_if<
113 std::is_convertible<typename std::remove_pointer<decltype(
114 data(std::declval<T&>()))>::type (*)[],
115 ElementType (*)[]>::value,
116 std::nullptr_t>::type>> : public std::true_type {
117};
118
119#if defined Vc_MSVC || (defined Vc_GCC && Vc_GCC < 0x50100) || defined Vc_ICC || !defined __cpp_constexpr || __cpp_constexpr < 201304
120#define Vc_CONSTEXPR
121#else
122#define Vc_CONSTEXPR constexpr
123#endif
124
125template <typename T, ptrdiff_t Extent> class span
126{
127public:
128 // constants and types
129 using element_type = T;
130 using value_type = typename std::remove_cv<T>::type;
131 using index_type = ptrdiff_t;
132 using difference_type = ptrdiff_t;
133 using pointer = T*;
134 using const_pointer = const T*; // not in standard
135 using reference = T&;
136 using const_reference = const T&; // not in standard
137 using iterator = pointer;
138 using const_iterator = const_pointer;
139 using reverse_iterator = std::reverse_iterator<iterator>;
140 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
141
142 static constexpr index_type extent = Extent;
143 static_assert(Extent >= 0, "Can't have a span with an extent < 0");
144
145 // [span.cons], span constructors, copy, assignment, and destructor
146 Vc_CONSTEXPR span() noexcept : data_{nullptr}
147 {
148 static_assert(Extent == 0,
149 "Can't default construct a statically sized span with size > 0");
150 }
151
152 Vc_CONSTEXPR span(const span&) noexcept = default;
153 Vc_CONSTEXPR span& operator=(const span&) noexcept = default;
154
155 Vc_CONSTEXPR span(pointer _ptr, index_type _count) : data_{_ptr}
156 {
157 (void)_count;
158 Vc_ASSERT(((void)"size mismatch in span's constructor (ptr, len)", Extent == _count));
159 }
160 Vc_CONSTEXPR span(pointer _f, pointer _l) : data_{_f}
161 {
162 (void)_l;
163 Vc_ASSERT(((void)"size mismatch in span's constructor (ptr, ptr)",
164 Extent == distance(_f, _l)));
165 }
166
167 Vc_CONSTEXPR span(element_type (&_arr)[Extent]) noexcept : data_{_arr} {}
168 Vc_CONSTEXPR span(array<value_type, Extent>& _arr) noexcept : data_{_arr.data()} {}
169 Vc_CONSTEXPR span(const array<value_type, Extent>& _arr) noexcept : data_{_arr.data()} {}
170
171 template <class Container>
172 inline Vc_CONSTEXPR span(
173 Container& _c,
174 typename std::enable_if<_is_span_compatible_container<Container, T>::value,
175 std::nullptr_t>::type = nullptr)
176 : data_{_std_data(_c)}
177 {
178 Vc_ASSERT(("size mismatch in span's constructor (container))",
179 Extent == _std_size(_c)));
180 }
181
182 template <class Container>
183 inline Vc_CONSTEXPR span(
184 const Container& _c,
185 typename std::enable_if<_is_span_compatible_container<const Container, T>::value,
186 std::nullptr_t>::type = nullptr)
187 : data_{_std_data(_c)}
188 {
189 Vc_ASSERT(("size mismatch in span's constructor (const container)",
190 Extent == _std_size(_c)));
191 }
192
193 template <class OtherElementType>
194 inline Vc_CONSTEXPR span(
195 const span<OtherElementType, Extent>& _other,
196 typename std::enable_if<
197 std::is_convertible<OtherElementType (*)[], element_type (*)[]>::value,
198 std::nullptr_t>::type = nullptr)
199 : data_{_other.data()}
200 {
201 }
202
203 template <class OtherElementType>
204 inline Vc_CONSTEXPR span(
205 const span<OtherElementType, dynamic_extent>& _other,
206 typename std::enable_if<
207 std::is_convertible<OtherElementType (*)[], element_type (*)[]>::value,
208 std::nullptr_t>::type = nullptr) noexcept
209 : data_{_other.data()}
210 {
211 Vc_ASSERT(("size mismatch in span's constructor (other span)",
212 Extent == _other.size()));
213 }
214
215 // ~span() noexcept = default;
216
217 template <ptrdiff_t Count>
218 inline Vc_CONSTEXPR span<element_type, Count> first() const noexcept
219 {
220 static_assert(Count >= 0, "Count must be >= 0 in span::first()");
221 static_assert(Count <= Extent, "Count out of range in span::first()");
222 return {data(), Count};
223 }
224
225 template <ptrdiff_t Count>
226 inline Vc_CONSTEXPR span<element_type, Count> last() const noexcept
227 {
228 static_assert(Count >= 0, "Count must be >= 0 in span::last()");
229 static_assert(Count <= Extent, "Count out of range in span::last()");
230 return {data() + size() - Count, Count};
231 }
232
233 Vc_CONSTEXPR span<element_type, dynamic_extent> first(index_type _count) const noexcept
234 {
235 Vc_ASSERT(("Count out of range in span::first(count)",
236 _count >= 0 && _count <= size()));
237 return {data(), _count};
238 }
239
240 Vc_CONSTEXPR span<element_type, dynamic_extent> last(index_type _count) const noexcept
241 {
242 Vc_ASSERT(
243 ("Count out of range in span::last(count)", _count >= 0 && _count <= size()));
244 return {data() + size() - _count, _count};
245 }
246
247#ifndef Vc_MSVC
248 // MSVC 190024215 fails with "error C2059: syntax error: '<end Parse>'" somewhere in
249 // this file. Unless someone needs this function on MSVC, I don't see a reason to
250 // invest time into working around their bugs.
251 template <ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
252 inline Vc_CONSTEXPR auto subspan() const noexcept
253 -> span<element_type, Count != dynamic_extent ? Count : Extent - Offset>
254 {
255 Vc_ASSERT(
256 ("Offset out of range in span::subspan()", Offset >= 0 && Offset <= size()));
257 return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
258 }
259
260 inline Vc_CONSTEXPR span<element_type, dynamic_extent> subspan(
261 index_type offset, index_type count = dynamic_extent) const noexcept
262 {
263 Vc_ASSERT(("Offset out of range in span::subspan(offset, count)",
264 offset >= 0 && offset <= size()));
265 Vc_ASSERT(("Count out of range in span::subspan(offset, count)",
266 (count >= 0 && count <= size()) || count == dynamic_extent));
267 if (count == dynamic_extent) {
268 return {data() + offset, size() - offset};
269 }
270 Vc_ASSERT(("count + offset out of range in span::subspan(offset, count)",
271 offset + count <= size()));
272 return {data() + offset, count};
273 }
274#endif // Vc_MSVC
275
276 Vc_CONSTEXPR index_type size() const noexcept { return Extent; }
277 Vc_CONSTEXPR index_type size_bytes() const noexcept
278 {
279 return Extent * sizeof(element_type);
280 }
281 Vc_CONSTEXPR bool empty() const noexcept { return Extent == 0; }
282
283 Vc_CONSTEXPR reference operator[](index_type _idx) const noexcept
284 {
285 Vc_ASSERT(("span<T,N>[] index out of bounds", _idx >= 0 && _idx < size()));
286 return data_[_idx];
287 }
288
289 Vc_CONSTEXPR reference operator()(index_type _idx) const noexcept
290 {
291 Vc_ASSERT(("span<T,N>() index out of bounds", _idx >= 0 && _idx < size()));
292 return data_[_idx];
293 }
294
295 Vc_CONSTEXPR pointer data() const noexcept { return data_; }
296
297 // [span.iter], span iterator support
298 Vc_CONSTEXPR iterator begin() const noexcept { return iterator(data()); }
299 Vc_CONSTEXPR iterator end() const noexcept { return iterator(data() + size()); }
300 Vc_CONSTEXPR const_iterator cbegin() const noexcept { return const_iterator(data()); }
301 Vc_CONSTEXPR const_iterator cend() const noexcept
302 {
303 return const_iterator(data() + size());
304 }
305 Vc_CONSTEXPR reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
306 Vc_CONSTEXPR reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
307 Vc_CONSTEXPR const_reverse_iterator crbegin() const noexcept
308 {
309 return const_reverse_iterator(cend());
310 }
311 Vc_CONSTEXPR const_reverse_iterator crend() const noexcept
312 {
313 return const_reverse_iterator(cbegin());
314 }
315
316 Vc_CONSTEXPR void swap(span& _other) noexcept
317 {
318 pointer _p = data_;
319 data_ = _other.data_;
320 _other.data_ = _p;
321 }
322
323#ifdef __cpp_lib_byte
324 span<const std::byte, Extent * sizeof(element_type)> _as_bytes() const noexcept
325 {
326 return {reinterpret_cast<const std::byte*>(data()), size_bytes()};
327 }
328
329 span<std::byte, Extent * sizeof(element_type)> _as_writeable_bytes() const noexcept
330 {
331 return {reinterpret_cast<std::byte*>(data()), size_bytes()};
332 }
333#endif // __cpp_lib_byte
334
335private:
336 pointer data_;
337};
338
339template <typename T> class span<T, dynamic_extent>
340{
341private:
342public:
343 // constants and types
344 using element_type = T;
345 using value_type = typename std::remove_cv<T>::type;
346 using index_type = ptrdiff_t;
347 using difference_type = ptrdiff_t;
348 using pointer = T*;
349 using const_pointer = const T*; // not in standard
350 using reference = T&;
351 using const_reference = const T&; // not in standard
352 using iterator = pointer;
353 using const_iterator = const_pointer;
354 using reverse_iterator = std::reverse_iterator<iterator>;
355 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
356
357 static constexpr index_type extent = dynamic_extent;
358
359 // [span.cons], span constructors, copy, assignment, and destructor
360 Vc_CONSTEXPR span() noexcept : data_{nullptr}, size_{0} {}
361
362 Vc_CONSTEXPR span(const span&) noexcept = default;
363 Vc_CONSTEXPR span& operator=(const span&) noexcept = default;
364
365 Vc_CONSTEXPR span(pointer _ptr, index_type _count) : data_{_ptr}, size_{_count} {}
366 Vc_CONSTEXPR span(pointer _f, pointer _l) : data_{_f}, size_{distance(_f, _l)} {}
367
368 template <size_t Sz>
369 inline Vc_CONSTEXPR span(element_type (&_arr)[Sz]) noexcept : data_{_arr}, size_{Sz}
370 {
371 }
372
373 template <size_t Sz>
374 inline Vc_CONSTEXPR span(array<value_type, Sz>& _arr) noexcept
375 : data_{_arr.data()}, size_{Sz}
376 {
377 }
378
379 template <size_t Sz>
380 inline Vc_CONSTEXPR span(const array<value_type, Sz>& _arr) noexcept
381 : data_{_arr.data()}, size_{Sz}
382 {
383 }
384
385 template <class Container>
386 inline Vc_CONSTEXPR span(
387 Container& _c,
388 typename std::enable_if<_is_span_compatible_container<Container, T>::value,
389 std::nullptr_t>::type = nullptr)
390 : data_{_std_data(_c)}, size_{index_type(_std_size(_c))}
391 {
392 }
393
394 template <class Container>
395 inline Vc_CONSTEXPR span(
396 const Container& _c,
397 typename std::enable_if<_is_span_compatible_container<const Container, T>::value,
398 std::nullptr_t>::type = nullptr)
399 : data_{_std_data(_c)}, size_{index_type(_std_size(_c))}
400 {
401 }
402
403 template <class OtherElementType, ptrdiff_t OtherExtent>
404 inline Vc_CONSTEXPR span(
405 const span<OtherElementType, OtherExtent>& _other,
406 typename std::enable_if<
407 std::is_convertible<OtherElementType (*)[], element_type (*)[]>::value,
408 std::nullptr_t>::type = nullptr) noexcept
409 : data_{_other.data()}, size_{_other.size()}
410 {
411 }
412
413 // ~span() noexcept = default;
414
415 template <ptrdiff_t Count>
416 inline Vc_CONSTEXPR span<element_type, Count> first() const noexcept
417 {
418 static_assert(Count >= 0, "");
419 Vc_ASSERT(("Count out of range in span::first()", Count <= size()));
420 return {data(), Count};
421 }
422
423 template <ptrdiff_t Count>
424 inline Vc_CONSTEXPR span<element_type, Count> last() const noexcept
425 {
426 static_assert(Count >= 0, "");
427 Vc_ASSERT(("Count out of range in span::last()", Count <= size()));
428 return {data() + size() - Count, Count};
429 }
430
431 Vc_CONSTEXPR span<element_type, dynamic_extent> first(index_type _count) const noexcept
432 {
433 Vc_ASSERT(("Count out of range in span::first(count)",
434 _count >= 0 && _count <= size()));
435 return {data(), _count};
436 }
437
438 Vc_CONSTEXPR span<element_type, dynamic_extent> last(index_type _count) const noexcept
439 {
440 Vc_ASSERT(
441 ("Count out of range in span::last(count)", _count >= 0 && _count <= size()));
442 return {data() + size() - _count, _count};
443 }
444
445 template <ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
446 inline Vc_CONSTEXPR span<T, dynamic_extent> subspan() const noexcept
447 {
448 Vc_ASSERT(
449 ("Offset out of range in span::subspan()", Offset >= 0 && Offset <= size()));
450 Vc_ASSERT(("Count out of range in span::subspan()",
451 Count == dynamic_extent || Offset + Count <= size()));
452 return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};
453 }
454
455 Vc_CONSTEXPR span<element_type, dynamic_extent> inline subspan(
456 index_type _offset, index_type _count = dynamic_extent) const noexcept
457 {
458 Vc_ASSERT(("Offset out of range in span::subspan(offset, count)",
459 _offset >= 0 && _offset <= size()));
460 Vc_ASSERT(("count out of range in span::subspan(offset, count)",
461 (_count >= 0 && _count <= size()) || _count == dynamic_extent));
462 if (_count == dynamic_extent)
463 return {data() + _offset, size() - _offset};
464 Vc_ASSERT(("Offset + count out of range in span::subspan(offset, count)",
465 _offset + _count <= size()));
466 return {data() + _offset, _count};
467 }
468
469 Vc_CONSTEXPR index_type size() const noexcept { return size_; }
470 Vc_CONSTEXPR index_type size_bytes() const noexcept
471 {
472 return size_ * sizeof(element_type);
473 }
474 Vc_CONSTEXPR bool empty() const noexcept { return size_ == 0; }
475
476 Vc_CONSTEXPR reference operator[](index_type _idx) const noexcept
477 {
478 Vc_ASSERT(("span<T>[] index out of bounds", _idx >= 0 && _idx < size()));
479 return data_[_idx];
480 }
481
482 Vc_CONSTEXPR reference operator()(index_type _idx) const noexcept
483 {
484 Vc_ASSERT(("span<T>() index out of bounds", _idx >= 0 && _idx < size()));
485 return data_[_idx];
486 }
487
488 Vc_CONSTEXPR pointer data() const noexcept { return data_; }
489
490 // [span.iter], span iterator support
491 Vc_CONSTEXPR iterator begin() const noexcept { return iterator(data()); }
492 Vc_CONSTEXPR iterator end() const noexcept { return iterator(data() + size()); }
493 Vc_CONSTEXPR const_iterator cbegin() const noexcept { return const_iterator(data()); }
494 Vc_CONSTEXPR const_iterator cend() const noexcept
495 {
496 return const_iterator(data() + size());
497 }
498 Vc_CONSTEXPR reverse_iterator rbegin() const noexcept { return reverse_iterator(end()); }
499 Vc_CONSTEXPR reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
500 Vc_CONSTEXPR const_reverse_iterator crbegin() const noexcept
501 {
502 return const_reverse_iterator(cend());
503 }
504 Vc_CONSTEXPR const_reverse_iterator crend() const noexcept
505 {
506 return const_reverse_iterator(cbegin());
507 }
508
509 Vc_CONSTEXPR void swap(span& _other) noexcept
510 {
511 pointer _p = data_;
512 data_ = _other.data_;
513 _other.data_ = _p;
514
515 index_type _sz = size_;
516 size_ = _other.size_;
517 _other.size_ = _sz;
518 }
519
520#ifdef __cpp_lib_byte
521// Disable _as_bytes() for older MSVC versions as it leads to a compilation error due to a compiler bug.
522// When parsing the return type, MSVC will instantiate the primary template of span<> and static_assert().
523#if _MSC_VER > 1928
524 span<const std::byte, dynamic_extent> _as_bytes() const noexcept
525 {
526 return {reinterpret_cast<const std::byte*>(data()), size_bytes()};
527 }
528
529 span<std::byte, dynamic_extent> _as_writeable_bytes() const noexcept
530 {
531 return {reinterpret_cast<std::byte*>(data()), size_bytes()};
532 }
533#endif
534#endif // __cpp_lib_byte
535
536private:
537 pointer data_;
538 index_type size_;
539};
540
541template <class T1, ptrdiff_t Extent1, class T2, ptrdiff_t Extent2>
542Vc_CONSTEXPR bool operator==(const span<T1, Extent1>& lhs, const span<T2, Extent2>& rhs)
543{
544 return equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
545}
546
547template <class T1, ptrdiff_t Extent1, class T2, ptrdiff_t Extent2>
548Vc_CONSTEXPR bool operator!=(const span<T1, Extent1>& lhs, const span<T2, Extent2>& rhs)
549{
550 return !(rhs == lhs);
551}
552
553template <class T1, ptrdiff_t Extent1, class T2, ptrdiff_t Extent2>
554Vc_CONSTEXPR bool operator<(const span<T1, Extent1>& lhs, const span<T2, Extent2>& rhs)
555{
556 return lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
557}
558
559template <class T1, ptrdiff_t Extent1, class T2, ptrdiff_t Extent2>
560Vc_CONSTEXPR bool operator<=(const span<T1, Extent1>& lhs, const span<T2, Extent2>& rhs)
561{
562 return !(rhs < lhs);
563}
564
565template <class T1, ptrdiff_t Extent1, class T2, ptrdiff_t Extent2>
566Vc_CONSTEXPR bool operator>(const span<T1, Extent1>& lhs, const span<T2, Extent2>& rhs)
567{
568 return rhs < lhs;
569}
570
571template <class T1, ptrdiff_t Extent1, class T2, ptrdiff_t Extent2>
572Vc_CONSTEXPR bool operator>=(const span<T1, Extent1>& lhs, const span<T2, Extent2>& rhs)
573{
574 return !(lhs < rhs);
575}
576
577// as_bytes & as_writeable_bytes
578template <class T, ptrdiff_t Extent>
579auto as_bytes(span<T, Extent> _s) noexcept -> decltype(_s._as_bytes())
580{
581 return _s._as_bytes();
582}
583
584template <class T, ptrdiff_t Extent>
585auto as_writeable_bytes(span<T, Extent> _s) noexcept ->
586 typename std::enable_if<!std::is_const<T>::value,
587 decltype(_s._as_writeable_bytes())>::type
588{
589 return _s._as_writeable_bytes();
590}
591
592template <class T, ptrdiff_t Extent>
593Vc_CONSTEXPR void swap(span<T, Extent>& lhs, span<T, Extent>& rhs) noexcept
594{
595 lhs.swap(rhs);
596}
597
598#undef Vc_CONSTEXPR
599
600// Deduction guides
601#ifdef __cpp_deduction_guides
602template <class T, size_t Sz> span(T (&)[Sz])->span<T, Sz>;
603
604template <class T, size_t Sz> span(array<T, Sz>&)->span<T, Sz>;
605
606template <class T, size_t Sz> span(const array<T, Sz>&)->span<const T, Sz>;
607
608template <class Container> span(Container&)->span<typename Container::value_type>;
609
610template <class Container>
611span(const Container&)->span<const typename Container::value_type>;
612#endif // __cpp_deduction_guides
613
614} // namespace Common
615
638template <typename T, ptrdiff_t Extent = dynamic_extent>
639using span = Common::AdaptSubscriptOperator<Common::span<T, Extent>>;
640
641namespace Traits
642{
643template <typename T, ptrdiff_t Extent>
644struct has_contiguous_storage_impl<Vc::span<T, Extent>> : public std::true_type {
645};
646template <typename T, ptrdiff_t Extent>
647struct has_contiguous_storage_impl<Vc::Common::span<T, Extent>> : public std::true_type {
648};
649} // namespace Traits
650
651} // namespace Vc_VERSIONED_NAMESPACE
652
653#endif // VC_COMMON_SPAN_H_
Common::AdaptSubscriptOperator< Common::span< T, Extent > > span
An adapted std::span with additional subscript operators supporting gather and scatter operations.
Definition span.h:639
Vector Classes Namespace.
Definition dox.h:585