Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
darray_impl.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstddef>
5#include <initializer_list>
6#include <type_traits>
7#include <utility>
8
9#include "gaia/cnt/impl/array_utils.h"
10#include "gaia/core/iterator.h"
11#include "gaia/core/utility.h"
12#include "gaia/mem/data_layout_policy.h"
13#include "gaia/mem/mem_sani.h"
14#include "gaia/mem/mem_utils.h"
15
16namespace gaia {
17 namespace cnt {
19 namespace darr_detail {
20 using difference_type = int32_t;
21 using size_type = uint32_t;
22 } // namespace darr_detail
24
27 template <typename T, typename Allocator = mem::DefaultAllocatorAdaptor>
28 class darr {
29 public:
31 using value_type = T;
33 using reference = T&;
35 using const_reference = const T&;
37 using pointer = T*;
39 using const_pointer = const T*;
43 using difference_type = darr_detail::difference_type;
45 using size_type = darr_detail::size_type;
46
57
59 static constexpr size_t value_size = sizeof(T);
60
61 private:
62 uint8_t* m_pData = nullptr;
63 size_type m_cnt = size_type(0);
64 size_type m_cap = size_type(0);
65
66 void try_grow() {
67 if (m_cnt == m_cap)
68 reserve(m_cap != 0 ? (m_cap * 3 + 1) / 2 : 4);
69 }
70
71 public:
74 darr(core::zero_t) noexcept {}
75
80 resize(count, value);
81 }
82
85 darr(size_type count) {
86 resize(count);
87 }
88
93 template <typename InputIt>
94 darr(InputIt first, InputIt last) {
95 const auto count = (size_type)core::distance(first, last);
96 resize(count);
97
98 if constexpr (std::is_pointer_v<InputIt>) {
99 for (size_type i = 0; i < count; ++i)
100 operator[](i) = first[i];
101 } else if constexpr (std::is_same_v<typename InputIt::iterator_category, core::random_access_iterator_tag>) {
102 for (size_type i = 0; i < count; ++i)
103 operator[](i) = *(first[i]);
104 } else {
105 size_type i = 0;
106 for (auto it = first; it != last; ++it)
107 operator[](i++) = *it;
108 }
109 }
110
113 darr(std::initializer_list<T> il): darr(il.begin(), il.end()) {}
114
117 darr(const darr& other): darr(other.begin(), other.end()) {}
118
121 darr(darr&& other) noexcept: m_pData(other.m_pData), m_cnt(other.m_cnt), m_cap(other.m_cap) {
122 other.m_pData = nullptr;
123 other.m_cnt = size_type(0);
124 other.m_cap = size_type(0);
125 }
126
130 darr& operator=(std::initializer_list<T> il) {
131 *this = darr(il.begin(), il.end());
132 return *this;
133 }
134
139 GAIA_ASSERT(core::addressof(other) != this);
140
141 resize(other.size());
142 mem::copy_elements<T, false>(
143 m_pData, (const uint8_t*)other.m_pData, other.size(), 0, capacity(), other.capacity());
144
145 return *this;
146 }
147
151 darr& operator=(darr&& other) noexcept {
152 GAIA_ASSERT(core::addressof(other) != this);
153
154 // Release previously allocated memory if there was anything
155 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
156
157 m_pData = other.m_pData;
158 m_cnt = other.m_cnt;
159 m_cap = other.m_cap;
160
161 other.m_pData = nullptr;
162 other.m_cnt = size_type(0);
163 other.m_cap = size_type(0);
164
165 return *this;
166 }
167
168 ~darr() {
169 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
170 }
171
172 GAIA_CLANG_WARNING_PUSH()
173 // Memory is aligned so we can silence this warning
174 GAIA_CLANG_WARNING_DISABLE("-Wcast-align")
175
178 GAIA_NODISCARD pointer data() noexcept {
179 return reinterpret_cast<pointer>(m_pData);
180 }
181
184 GAIA_NODISCARD const_pointer data() const noexcept {
185 return reinterpret_cast<const_pointer>(m_pData);
186 }
187
191 GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept {
192 GAIA_ASSERT(pos < size());
193 return view_policy::set({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
194 }
195
199 GAIA_NODISCARD decltype(auto) operator[](size_type pos) const noexcept {
200 GAIA_ASSERT(pos < size());
201 return view_policy::get({(typename view_policy::TargetCastType)m_pData, capacity()}, pos);
202 }
203
204 GAIA_CLANG_WARNING_POP()
205
206
209 if (cap <= m_cap)
210 return;
211
212 auto* pDataOld = m_pData;
213 m_pData = view_policy::template alloc<Allocator>(cap);
214 if (pDataOld != nullptr) {
215 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, cap, m_cnt);
216 mem::move_ctor_elements<T>(m_pData, pDataOld, m_cnt);
217 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
218 }
219
220 m_cap = cap;
221 }
222
226 void resize(size_type count, ResizePolicy policy = ResizePolicy::Exact) {
227 if (count == m_cnt)
228 return;
229
230 // Fresh allocation
231 if (m_pData == nullptr) {
232 if (count > 0) {
233 m_pData = view_policy::template alloc<Allocator>(count);
234 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, count, count);
235 core::call_ctor_n(data(), count);
236 m_cap = count;
237 m_cnt = count;
238 }
239 return;
240 }
241
242 // Resizing to a smaller size
243 if (count < m_cnt) {
244 // Destroy elements at the end
245 core::call_dtor_n(&data()[count], m_cnt - count);
246 GAIA_MEM_SANI_POP_N(value_size, m_pData, m_cap, m_cnt, m_cnt - count);
247
248 m_cnt = count;
249 return;
250 }
251
252 // Resizing to a bigger size but still within allocated capacity
253 if (count <= m_cap) {
254 // Construct new elements
255 GAIA_MEM_SANI_PUSH_N(value_size, m_pData, m_cap, m_cnt, count - m_cnt);
256 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
257
258 m_cnt = count;
259 return;
260 }
261
262 const auto cap = detail::resize_capacity(policy, m_cap, count);
263 auto* pDataOld = m_pData;
264 m_pData = view_policy::template alloc<Allocator>(cap);
265 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, cap, count);
266 // Move old data to the new location
267 mem::move_ctor_elements<T>(m_pData, pDataOld, m_cnt);
268 // Default-construct new items
269 core::call_ctor_n(&data()[m_cnt], count - m_cnt);
270 // Release old memory
271 view_policy::template free<Allocator>(pDataOld, m_cap, m_cnt);
272
273 m_cap = cap;
274 m_cnt = count;
275 }
276
281 void resize(size_type count, const_reference value, ResizePolicy policy = ResizePolicy::Exact) {
282 if (count <= m_cnt) {
283 resize(count, policy);
284 return;
285 }
286 const auto oldCount = m_cnt;
287 if constexpr (std::is_copy_constructible_v<value_type>) {
288 const value_type valueCopy = value;
289 resize(count, policy);
290 for (size_type i = oldCount; i < m_cnt; ++i)
291 operator[](i) = valueCopy;
292 } else {
294 valueCopy = value;
295 resize(count, policy);
296 for (size_type i = oldCount; i < m_cnt; ++i)
297 operator[](i) = valueCopy;
298 }
299 }
300
303 void push_back(const T& arg) {
305 }
306
309 void push_back(T&& arg) {
310 emplace_back(GAIA_MOV(arg));
311 }
312
317 template <typename... Args>
318 decltype(auto) emplace_back(Args&&... args) {
319 if GAIA_UNLIKELY (m_cnt == m_cap) {
320 const auto cap = m_cap != 0 ? (m_cap * 3 + 1) / 2 : 4;
321 auto* pDataNew = view_policy::template alloc<Allocator>(cap);
322 GAIA_MEM_SANI_ADD_BLOCK(value_size, pDataNew, cap, m_cnt + 1);
324 core::call_ctor(&static_cast<T*>(static_cast<void*>(pDataNew))[m_cnt], GAIA_FWD(args)...);
325 mem::move_ctor_elements<T>(pDataNew, m_pData, m_cnt);
326 view_policy::template free<Allocator>(m_pData, m_cap, m_cnt);
327 m_pData = pDataNew;
328 m_cap = cap;
329 } else {
330 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
331 core::call_ctor(&data()[m_cnt], GAIA_FWD(args)...);
332 }
333 return (reference)data()[m_cnt++];
334 }
335
338 GAIA_ASSERT(!empty());
339
340 auto* ptr = &data()[m_cnt - 1];
341 core::call_dtor(ptr);
342 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
343
344 --m_cnt;
345 }
346
352 GAIA_ASSERT(pos >= data());
353 GAIA_ASSERT(pos <= iterator(data() + size()));
354
355 T value(arg);
356 const auto idxSrc = (size_type)core::distance(begin(), pos);
357 try_grow();
358 const auto idxDst = (size_type)core::distance(begin(), end());
359
360 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
361 auto* ptr = &data()[idxSrc];
362 if (idxSrc == idxDst) {
363 core::call_ctor(ptr, std::move_if_noexcept(value));
364 } else {
365 mem::move_ctor_elements<T>((uint8_t*)&data()[idxDst], (uint8_t*)&data()[idxDst - 1], 1);
366 mem::shift_elements_right<T, false>(m_pData, idxDst - 1, idxSrc, m_cap);
367 mem::move_element<T, false>((uint8_t*)ptr, (uint8_t*)&value, 0, 0, 1, 1);
368 }
369
370 ++m_cnt;
371
372 return iterator(&data()[idxSrc]);
373 }
374
380 GAIA_ASSERT(pos >= data());
381 GAIA_ASSERT(pos <= iterator(data() + size()));
382
383 T value(GAIA_MOV(arg));
384 const auto idxSrc = (size_type)core::distance(begin(), pos);
385 try_grow();
386 const auto idxDst = (size_type)core::distance(begin(), end());
387
388 GAIA_MEM_SANI_PUSH(value_size, m_pData, m_cap, m_cnt);
389 auto* ptr = &data()[idxSrc];
390 if (idxSrc == idxDst) {
391 core::call_ctor(ptr, std::move_if_noexcept(value));
392 } else {
393 mem::move_ctor_elements<T>((uint8_t*)&data()[idxDst], (uint8_t*)&data()[idxDst - 1], 1);
394 mem::shift_elements_right<T, false>(m_pData, idxDst - 1, idxSrc, m_cap);
395 mem::move_element<T, false>((uint8_t*)ptr, (uint8_t*)&value, 0, 0, 1, 1);
396 }
397
398 ++m_cnt;
399
400 return iterator(&data()[idxSrc]);
401 }
402
407 GAIA_ASSERT(pos >= data());
408 GAIA_ASSERT(empty() || (pos < iterator(data() + size())));
409
410 if (empty())
411 return end();
412
413 const auto idxSrc = (size_type)core::distance(begin(), pos);
414 const auto idxDst = (size_type)core::distance(begin(), end()) - 1;
415
416 mem::shift_elements_left<T, false>(m_pData, idxDst, idxSrc, m_cap);
417 // Destroy if it's the last element
418 auto* ptr = &data()[m_cnt - 1];
419 core::call_dtor(ptr);
420 GAIA_MEM_SANI_POP(value_size, m_pData, m_cap, m_cnt);
421
422 --m_cnt;
423
424 return iterator(&data()[idxSrc]);
425 }
426
431 iterator erase(iterator first, iterator last) noexcept {
432 GAIA_ASSERT(first >= data())
433 GAIA_ASSERT(empty() || (first < iterator(data() + size())));
434 GAIA_ASSERT(last > first);
435 GAIA_ASSERT(last <= iterator(data() + size()));
436
437 if (empty())
438 return end();
439
440 const auto idxSrc = (size_type)core::distance(begin(), first);
441 const auto idxDst = size();
442 const auto cnt = (size_type)(last - first);
443
444 mem::shift_elements_left_fast<T, false>(m_pData, idxDst, idxSrc, cnt, m_cap);
445 // Destroy if it's the last element
446 auto* ptr = &data()[m_cnt - cnt];
447 core::call_dtor_n(ptr, cnt);
448 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, m_cnt, cnt);
449
450 m_cnt -= cnt;
451
452 return iterator(&data()[idxSrc]);
453 }
454
457 resize(0);
458 }
459
462 const auto cap = capacity();
463 const auto cnt = size();
464 if (cap == cnt)
465 return;
466
467 auto* pDataOld = m_pData;
468 m_pData = cnt != 0 ? view_policy::template alloc<Allocator>(cnt) : nullptr;
469 if (cnt != 0) {
470 GAIA_MEM_SANI_ADD_BLOCK(value_size, m_pData, cnt, cnt);
471 mem::move_ctor_elements<T>(m_pData, pDataOld, cnt);
472 }
473 view_policy::template free<Allocator>(pDataOld, cap, cnt);
474 m_cap = cnt;
475 }
476
482 template <typename Func>
483 auto retain(Func&& func) {
484 const auto oldCount = m_cnt;
485 const auto newSize = detail::retain_array<false>(*this, m_cnt, func);
486 GAIA_MEM_SANI_POP_N(value_size, data(), m_cap, oldCount, oldCount - m_cnt);
487 return newSize;
488 }
489
492 GAIA_NODISCARD size_type size() const noexcept {
493 return m_cnt;
494 }
495
498 GAIA_NODISCARD bool empty() const noexcept {
499 return size() == 0;
500 }
501
504 GAIA_NODISCARD size_type capacity() const noexcept {
505 return m_cap;
506 }
507
510 GAIA_NODISCARD size_type max_size() const noexcept {
511 return static_cast<size_type>(-1);
512 }
513
516 GAIA_NODISCARD decltype(auto) front() noexcept {
517 GAIA_ASSERT(!empty());
518 return (reference)*begin();
519 }
520
523 GAIA_NODISCARD decltype(auto) front() const noexcept {
524 GAIA_ASSERT(!empty());
525 return (const_reference)*begin();
526 }
527
530 GAIA_NODISCARD decltype(auto) back() noexcept {
531 GAIA_ASSERT(!empty());
532 return (reference)(operator[](m_cnt - 1));
533 }
534
537 GAIA_NODISCARD decltype(auto) back() const noexcept {
538 GAIA_ASSERT(!empty());
539 return (const_reference) operator[](m_cnt - 1);
540 }
541
544 GAIA_NODISCARD auto begin() noexcept {
545 return iterator(data());
546 }
547
550 GAIA_NODISCARD auto begin() const noexcept {
551 return cbegin();
552 }
553
556 GAIA_NODISCARD auto cbegin() const noexcept {
557 return const_iterator(data());
558 }
559
562 GAIA_NODISCARD auto rbegin() noexcept {
563 return reverse_iterator(end());
564 }
565
568 GAIA_NODISCARD auto rbegin() const noexcept {
569 return const_reverse_iterator(end());
570 }
571
574 GAIA_NODISCARD auto crbegin() const noexcept {
576 }
577
580 GAIA_NODISCARD auto end() noexcept {
581 return iterator(data() + size());
582 }
583
586 GAIA_NODISCARD auto end() const noexcept {
587 return const_iterator(data() + size());
588 }
589
592 GAIA_NODISCARD auto cend() const noexcept {
593 return const_iterator(data() + size());
594 }
595
598 GAIA_NODISCARD auto rend() noexcept {
599 return reverse_iterator(begin());
600 }
601
604 GAIA_NODISCARD auto rend() const noexcept {
606 }
607
610 GAIA_NODISCARD auto crend() const noexcept {
612 }
613
617 GAIA_NODISCARD bool operator==(const darr& other) const noexcept {
618 if (m_cnt != other.m_cnt)
619 return false;
620 const size_type n = size();
621 for (size_type i = 0; i < n; ++i)
622 if (!(operator[](i) == other[i]))
623 return false;
624 return true;
625 }
626
630 GAIA_NODISCARD constexpr bool operator!=(const darr& other) const noexcept {
631 return !operator==(other);
632 }
633 };
634 } // namespace cnt
635
636} // namespace gaia
Array of elements of type.
Definition darray_ext_impl.h:31
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_ext_impl.h:561
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_ext_impl.h:613
GAIA_NODISCARD auto end() noexcept
Returns an iterator one past the last element.
Definition darray_ext_impl.h:649
darr_ext_detail::size_type size_type
Unsigned type used for sizes and indices.
Definition darray_ext_impl.h:50
darr_ext_detail::difference_type difference_type
Type used for iterator differences.
Definition darray_ext_impl.h:48
GAIA_NODISCARD size_type capacity() const noexcept
Returns the number of elements that fit without reallocation.
Definition darray_ext_impl.h:573
Array with variable size of elements of type.
Definition darray_impl.h:28
darr(const darr &other)
Copy-constructs a container.
Definition darray_impl.h:117
iterator insert(iterator pos, T &&arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:379
iterator insert(iterator pos, const T &arg)
Insert the element to the position given by iterator pos.
Definition darray_impl.h:351
GAIA_NODISCARD auto rbegin() noexcept
Returns a reverse traversal iterator to the last element.
Definition darray_impl.h:562
GAIA_NODISCARD auto begin() const noexcept
Returns an iterator to the first element.
Definition darray_impl.h:550
GAIA_NODISCARD auto rbegin() const noexcept
Returns a reverse traversal iterator to the last element.
Definition darray_impl.h:568
void resize(size_type count, ResizePolicy policy=ResizePolicy::Exact)
Changes the number of elements.
Definition darray_impl.h:226
const_pointer const_iterator
Read-only random-access iterator type.
Definition darray_impl.h:50
GAIA_NODISCARD auto cend() const noexcept
Returns a read-only iterator one past the last element.
Definition darray_impl.h:592
decltype(auto) emplace_back(Args &&... args)
Constructs and appends an element.
Definition darray_impl.h:318
darr_detail::size_type size_type
Unsigned type used for sizes and indices.
Definition darray_impl.h:45
GAIA_NODISCARD decltype(auto) operator[](size_type pos) noexcept
Accesses an element without bounds checking in optimized builds.
Definition darray_impl.h:191
GAIA_NODISCARD decltype(auto) back() const noexcept
Accesses the last element.
Definition darray_impl.h:537
GAIA_NODISCARD auto cbegin() const noexcept
Returns a read-only iterator to the first element.
Definition darray_impl.h:556
darr & operator=(const darr &other)
Copy-assigns the container.
Definition darray_impl.h:138
darr(InputIt first, InputIt last)
Constructs a container from an iterator range.
Definition darray_impl.h:94
GAIA_NODISCARD decltype(auto) front() const noexcept
Accesses the first element.
Definition darray_impl.h:523
GAIA_NODISCARD size_type capacity() const noexcept
Returns the number of elements that fit without reallocation.
Definition darray_impl.h:504
darr & operator=(darr &&other) noexcept
Move-assigns the container.
Definition darray_impl.h:151
void reserve(size_type cap)
Ensures storage for at least the requested number of elements.
Definition darray_impl.h:208
GAIA_NODISCARD const_pointer data() const noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:184
GAIA_NODISCARD size_type size() const noexcept
Returns the number of elements.
Definition darray_impl.h:492
GAIA_NODISCARD decltype(auto) back() noexcept
Accesses the last element.
Definition darray_impl.h:530
void clear() noexcept
Removes all elements.
Definition darray_impl.h:456
GAIA_NODISCARD auto begin() noexcept
Returns an iterator to the first element.
Definition darray_impl.h:544
void shrink_to_fit()
Reduces allocated storage to match the current size when possible.
Definition darray_impl.h:461
GAIA_NODISCARD auto crend() const noexcept
Returns the read-only reverse traversal sentinel preceding the first element.
Definition darray_impl.h:610
iterator erase(iterator first, iterator last) noexcept
Removes the elements in the range [first, last)
Definition darray_impl.h:431
darr(size_type count, const_reference value)
Constructs a container with copies of a value.
Definition darray_impl.h:79
darr(size_type count)
Constructs a container with the requested number of value-initialized elements.
Definition darray_impl.h:85
static constexpr size_t value_size
Size of one element in bytes.
Definition darray_impl.h:59
core::reverse_iterator< iterator, difference_type > reverse_iterator
Mutable reverse random-access iterator type.
Definition darray_impl.h:52
GAIA_NODISCARD auto rend() const noexcept
Returns the reverse traversal sentinel preceding the first element.
Definition darray_impl.h:604
darr & operator=(std::initializer_list< T > il)
Replaces the elements from an initializer list.
Definition darray_impl.h:130
GAIA_NODISCARD auto rend() noexcept
Returns the reverse traversal sentinel preceding the first element.
Definition darray_impl.h:598
core::reverse_iterator< const_iterator, difference_type > const_reverse_iterator
Read-only reverse random-access iterator type.
Definition darray_impl.h:54
auto retain(Func &&func)
Removes all elements that fail the predicate. The predicate may modify its element but must not chang...
Definition darray_impl.h:483
GAIA_NODISCARD bool operator==(const darr &other) const noexcept
Compares two containers element by element.
Definition darray_impl.h:617
GAIA_NODISCARD decltype(auto) front() noexcept
Accesses the first element.
Definition darray_impl.h:516
void resize(size_type count, const_reference value, ResizePolicy policy=ResizePolicy::Exact)
Changes the size and initializes new elements from a value.
Definition darray_impl.h:281
iterator erase(iterator pos) noexcept
Removes the element at pos.
Definition darray_impl.h:406
void push_back(T &&arg)
Appends an element.
Definition darray_impl.h:309
darr(std::initializer_list< T > il)
Constructs a container from an initializer list.
Definition darray_impl.h:113
pointer iterator
Mutable random-access iterator type.
Definition darray_impl.h:48
darr_detail::difference_type difference_type
Type used for iterator differences.
Definition darray_impl.h:43
GAIA_NODISCARD bool empty() const noexcept
Checks whether the container has no elements.
Definition darray_impl.h:498
void pop_back() noexcept
Removes the last element.
Definition darray_impl.h:337
darr(darr &&other) noexcept
Move-constructs a container.
Definition darray_impl.h:121
GAIA_NODISCARD pointer data() noexcept
Returns a pointer to the element storage.
Definition darray_impl.h:178
void push_back(const T &arg)
Appends an element.
Definition darray_impl.h:303
GAIA_NODISCARD auto crbegin() const noexcept
Returns a read-only reverse traversal iterator to the last element.
Definition darray_impl.h:574
T * pointer
Mutable element pointer type.
Definition darray_impl.h:37
GAIA_NODISCARD size_type max_size() const noexcept
Returns the maximum number of elements supported by this container.
Definition darray_impl.h:510
GAIA_NODISCARD constexpr bool operator!=(const darr &other) const noexcept
Checks whether two containers differ.
Definition darray_impl.h:630
const T * const_pointer
Read-only element pointer type.
Definition darray_impl.h:39
GAIA_NODISCARD auto end() noexcept
Returns an iterator one past the last element.
Definition darray_impl.h:580
GAIA_NODISCARD auto end() const noexcept
Returns an iterator one past the last element.
Definition darray_impl.h:586
Random-access iterator that traverses an underlying iterator in reverse. The stored iterator denotes ...
Definition iterator.h:24
View policy for accessing and storing data in the AoS way. Good for random access and when accessing ...
Definition data_layout_policy.h:160
std::add_pointer_t< ValueType > TargetCastType
Pointer type used to address stored values.
Definition data_layout_policy.h:162
GAIA_NODISCARD static constexpr ValueType & set(std::span< ValueType > s, size_t idx) noexcept
Returns a mutable value reference from an AoS span.
Definition data_layout_policy.h:234
GAIA_NODISCARD static constexpr const ValueType & get(std::span< const ValueType > s, size_t idx) noexcept
Returns a read-only value reference from an AoS span.
Definition data_layout_policy.h:226