plugify 1.2.8
Loading...
Searching...
No Matches
inplace_vector.hpp
1#pragma once
2
3#include "plg/config.hpp"
4
5#if __has_include(<inplace_vector>)
6#include <inplace_vector>
7#if defined(__cpp_lib_inplace_vector) && __cpp_lib_inplace_vector >= 202406L
8#define PLUGIFY_HAS_STD_INPLACE_VECTOR 1
9#else
10#define PLUGIFY_HAS_STD_INPLACE_VECTOR 0
11#endif
12#else
13#define PLUGIFY_HAS_STD_INPLACE_VECTOR 0
14#endif
15
16#if !PLUGIFY_HAS_STD_INPLACE_VECTOR
17#include <algorithm>
18#include <cstring>
19#include <initializer_list>
20#include <memory>
21#include <new>
22#include <type_traits>
23
24#if PLUGIFY_CPP_VERSION >= 202002L
25#include <compare>
26#include <ranges>
27#endif
28
29#ifndef PLUGIFY_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF
30#if defined(__cpp_impl_trivially_relocatable) && defined(__cpp_lib_trivially_relocatable)
31#define PLUGIFY_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF(x) [[trivially_relocatable(x)]]
32#else
33#define PLUGIFY_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF(x)
34#endif // __cpp_impl_trivially_relocatable
35#endif // PLUGIFY_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF
36
37// from https://github.com/Quuxplusone/SG14
38namespace plg {
39 namespace detail {
40
41 template<class T, bool = (std::is_copy_constructible_v<T> && std::is_copy_assignable_v<T>),
42 bool = (std::is_move_constructible_v<T> && std::is_move_assignable_v<T>)>
44 // Base for copyable types
45 };
46
47 template<class T, bool Copyable>
49 // Base for immobile types like std::mutex
50 explicit ipvbase_assignable() = default;
53 void operator=(ipvbase_assignable&&) = delete;
54 void operator=(const ipvbase_assignable&) = delete;
55 ~ipvbase_assignable() = default;
56 };
57
58 template<class T>
60 explicit ipvbase_assignable() = default;
63 void operator=(const ipvbase_assignable&) = delete;
64 ipvbase_assignable& operator=(ipvbase_assignable&&) = default;
65 ~ipvbase_assignable() = default;
66 };
67
68 template<class T, size_t N, class = void>
69 struct PLUGIFY_INPLACE_VECTOR_TRIVIALLY_RELOCATABLE_IF(std::is_trivially_relocatable_v<T>) ipvbase
70 {
71 size_t size_ = 0;
72 union {
73 [[maybe_unused]] char dummy_;
74 T data_[N];
75 };
76
77 constexpr T *base_data() { return data_; }
78 constexpr const T *base_data() const { return data_; }
79 constexpr void set_size(size_t n) { size_ = n; }
80
81 constexpr explicit ipvbase() noexcept {}
82 ipvbase(const ipvbase& rhs)
83 noexcept(std::is_nothrow_copy_constructible_v<T>)
84 {
85 if constexpr (std::is_trivially_copy_constructible_v<T>) {
86 std::memmove((void*)this, (const void*)std::addressof(rhs), sizeof(ipvbase));
87 } else {
88 std::uninitialized_copy_n(rhs.data_, rhs.size_, data_);
89 size_ = rhs.size_;
90 }
91 }
92 ipvbase(ipvbase&& rhs)
93 noexcept(std::is_nothrow_move_constructible_v<T>
94#if defined(__cpp_lib_trivially_relocatable)
95 || std::is_trivially_relocatable_v<T>
96#endif // __cpp_lib_trivially_relocatable
97 )
98 {
99 if constexpr (std::is_trivially_move_constructible_v<T>) {
100 std::memmove((void*)this, (const void*)std::addressof(rhs), sizeof(ipvbase));
101#if defined(__cpp_lib_trivially_relocatable)
102 } else if constexpr (std::is_trivially_relocatable_v<T>) {
103 std::uninitialized_relocate_n(rhs.data_, rhs.size_, data_);
104 size_ = rhs.size_;
105 rhs.size_ = 0;
106#endif // __cpp_lib_trivially_relocatable
107 } else {
108 std::uninitialized_move_n(rhs.data_, rhs.size_, data_);
109 size_ = rhs.size_;
110 }
111 }
112 void operator=(const ipvbase& rhs)
113 noexcept(std::is_nothrow_copy_constructible_v<T> && std::is_nothrow_copy_assignable_v<T>)
114 {
115 if constexpr (std::is_trivially_copy_constructible_v<T> && std::is_trivially_copy_assignable_v<T> && std::is_trivially_destructible_v<T>) {
116 std::memmove((void*)this, (const void*)std::addressof(rhs), sizeof(ipvbase));
117 } else if (this == std::addressof(rhs)) {
118 // do nothing
119 } else if (rhs.size_ <= size_) {
120 std::copy(rhs.data_, rhs.data_ + rhs.size_, data_);
121 std::destroy(data_ + rhs.size_, data_ + size_);
122 size_ = rhs.size_;
123 } else {
124 std::copy(rhs.data_, rhs.data_ + size_, data_);
125 std::uninitialized_copy(rhs.data_ + size_, rhs.data_ + rhs.size_, data_ + size_);
126 size_ = rhs.size_;
127 }
128 }
129 void operator=(ipvbase&& rhs)
130 noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T>)
131 {
132 if constexpr (std::is_trivially_move_constructible_v<T> && std::is_trivially_move_assignable_v<T> && std::is_trivially_destructible_v<T>) {
133 std::memmove((void*)this, (const void*)std::addressof(rhs), sizeof(ipvbase));
134 } else if (this == std::addressof(rhs)) {
135 // do nothing
136 } else if (rhs.size_ <= size_) {
137 std::move(rhs.data_, rhs.data_ + rhs.size_, data_);
138 std::destroy(data_ + rhs.size_, data_ + size_);
139 size_ = rhs.size_;
140 } else {
141 std::move(rhs.data_, rhs.data_ + size_, data_);
142#if defined(__cpp_lib_trivially_relocatable)
143 if constexpr (std::is_trivially_relocatable_v<T>) {
144 std::uninitialized_relocate(rhs.data_ + size_, rhs.data_ + rhs.size_, data_ + size_);
145 std::swap(rhs.size_, size_);
146 return;
147 }
148#endif // __cpp_lib_trivially_relocatable
149 std::uninitialized_move(rhs.data_ + size_, rhs.data_ + rhs.size_, data_ + size_);
150 size_ = rhs.size_;
151 }
152 }
153
154#if __cpp_concepts >= 202002L
155 ipvbase(const ipvbase&) requires std::is_trivially_copy_constructible_v<T> = default;
156 ipvbase(ipvbase&&) requires std::is_trivially_move_constructible_v<T> = default;
157 ipvbase& operator=(const ipvbase&) requires std::is_trivially_copy_constructible_v<T> && std::is_trivially_copy_assignable_v<T> && std::is_trivially_destructible_v<T> = default;
158 ipvbase& operator=(ipvbase&&) requires std::is_trivially_move_constructible_v<T> && std::is_trivially_move_assignable_v<T> && std::is_trivially_destructible_v<T> = default;
159 ~ipvbase() requires std::is_trivially_destructible_v<T> = default;
160#endif // __cpp_concepts >= 202002L
161
162#if PLUGIFY_CPP_VERSION >= 202002L
163 constexpr
164#endif // PLUGIFY_CPP_VERSION >= 202002L
165 ~ipvbase() {
166 std::destroy(data_, data_ + size_);
167 }
168 };
169
170 template<class T>
172 static constexpr size_t size_ = 0;
173 constexpr T *base_data() { return nullptr; }
174 constexpr const T *base_data() const { return nullptr; }
175 constexpr void set_size(size_t) { }
176 };
177
178 template<class T, size_t N>
180 size_t size_ = 0;
181 union {
182 [[maybe_unused]] char dummy_;
183 T data_[N];
184 };
185 constexpr explicit ipvbase_trivial() {}
186 constexpr T *base_data() { return data_; }
187 constexpr const T *base_data() const { return data_; }
188 constexpr void set_size(size_t n) { size_ = n; }
189 };
190
191 template<class T, size_t N>
192 using ipvbase_t = std::conditional_t<
193 N == 0,
195 std::conditional_t<
196 std::is_trivially_copyable_v<T>,
199 >
200 >;
201 } // namespace detail
202
203 template<class T, size_t N>
204 class inplace_vector : detail::ipvbase_assignable<T>, detail::ipvbase_t<T, N> {
205 using detail::ipvbase_t<T, N>::size_;
206 using detail::ipvbase_t<T, N>::set_size;
207 public:
208 using value_type = T;
209 using pointer = T*;
210 using const_pointer = const T*;
211 using reference = T&;
212 using const_reference = const T&;
213 using size_type = size_t;
215 using iterator = T*;
216 using const_iterator = const T*;
217 using reverse_iterator = std::reverse_iterator<iterator>;
218 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
219
220 // [inplace.vector.cons]
221
222 inplace_vector() = default;
223 inplace_vector(inplace_vector&&) = default;
224 inplace_vector(const inplace_vector&) = default;
225 inplace_vector& operator=(inplace_vector&&) = default;
226 inplace_vector& operator=(const inplace_vector&) = default;
227 inplace_vector& operator=(std::initializer_list<value_type> il)
228 requires std::is_copy_constructible_v<T>
229 {
230 assign(il.begin(), il.end());
231 return *this;
232 }
233
234 constexpr inplace_vector(std::initializer_list<value_type> il)
235 requires std::copy_constructible<T> : inplace_vector(il.begin(), il.end()) { }
236 constexpr explicit inplace_vector(size_t n)
237 requires std::default_initializable<T>
238 {
239 if (n > N) {
240 throw_bad_alloc();
241 }
242 std::uninitialized_value_construct_n(data(), n);
243 set_size(n);
244 }
245 constexpr explicit inplace_vector(size_t n, const value_type& value)
246 requires std::copy_constructible<T>
247 {
248 assign(n, value);
249 }
250
251 template<std::input_iterator InputIterator>
252 requires std::constructible_from<T, typename std::iterator_traits<InputIterator>::value_type>
254 {
255 if constexpr (std::random_access_iterator<InputIterator>) {
256 size_t n = static_cast<size_type>(std::distance(first, last));
257 if (n > N) {
258 throw_bad_alloc();
259 }
260 std::uninitialized_copy_n(first, n, data());
261 set_size(n);
262 } else {
263 for (; first != last; ++first) {
264 emplace_back(*first);
265 }
266 }
267 }
268
269 constexpr void assign(std::initializer_list<value_type> il)
270 requires std::is_copy_constructible_v<T>
271 {
272 assign(il.begin(), il.end());
273 }
274
275 constexpr void assign(size_t n, const value_type& value)
276 requires std::is_copy_constructible_v<T>
277 {
278 if (n > N) {
279 throw_bad_alloc();
280 } else if (size_ >= n) {
281 std::fill_n(data(), n, value);
282 std::destroy(data() + n, data() + size_);
283 } else {
284 std::fill_n(data(), size_, value);
285 std::uninitialized_fill_n(data() + size_, n - size_, value);
286 }
287 set_size(n);
288 }
289
290 template<std::input_iterator InputIterator>
291 requires std::is_constructible_v<T, typename std::iterator_traits<InputIterator>::value_type>
292 constexpr void assign(InputIterator first, InputIterator last) {
293 const size_type n = static_cast<size_type>(std::distance(first, last));
294 if (n > N) {
295 throw_bad_alloc();
296 } else if (size_ >= n) {
297 std::copy(first, last, data());
298 std::destroy(data() + n, data() + size_);
299 } else {
300 std::copy(first, first + size_, data());
301 std::uninitialized_copy(first + size_, last, data() + size_);
302 }
303 set_size(n);
304 }
305
306 #if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L
307 template<std::ranges::input_range R>
308 requires std::convertible_to<std::ranges::range_reference_t<R>, value_type>
309 constexpr explicit inplace_vector(std::from_range_t, R&& rg) {
310 if constexpr (std::ranges::sized_range<R>) {
311 size_t n = std::ranges::size(rg);
312 if (n > N) {
313 throw_bad_alloc();
314 }
315 std::ranges::uninitialized_copy_n(std::ranges::begin(rg), n, data(), std::unreachable_sentinel);
316 set_size(n);
317 } else {
318 for (auto&& e : rg) {
319 emplace_back(decltype(e)(e));
320 }
321 }
322 }
323
324 template<std::ranges::input_range R>
325 requires std::convertible_to<std::ranges::range_reference_t<R>, value_type>
326 constexpr void assign_range(R&& rg) {
327 auto first = std::ranges::begin(rg);
328 auto last = std::ranges::end(rg);
329 size_t n = std::ranges::size(rg);
330 if (n > N) {
331 throw_bad_alloc();
332 } else if (size_ >= n) {
333 std::ranges::copy(first, last, data());
334 std::destroy(data() + n, data() + size_);
335 } else {
336 auto mid = std::ranges::next(first, size_, last);
337 std::ranges::copy(first, mid, data());
338 std::ranges::uninitialized_copy(mid, last, data() + size_);
339 }
340 set_size(n);
341 }
342 #endif // __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L
343
344 // iterators
345
346 constexpr iterator begin() noexcept { return data(); }
347 constexpr iterator end() noexcept { return data() + size_; }
348 constexpr const_iterator begin() const noexcept { return data(); }
349 constexpr const_iterator end() const noexcept { return data() + size_; }
350 constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
351 constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
352 constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
353 constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
354 constexpr const_iterator cbegin() const noexcept { return data(); }
355 constexpr const_iterator cend() const noexcept { return data() + size_; }
356 constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
357 constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
358
359 constexpr void resize(size_type n)
360 requires std::is_default_constructible_v<T>
361 {
362 if (n > N) {
363 throw_bad_alloc();
364 } else if (n < size_) {
365 std::destroy(data() + n, data() + size_);
366 set_size(n);
367 } else {
368 std::uninitialized_value_construct(data() + size_, data() + n);
369 set_size(size_ + n);
370 }
371 }
372
373 constexpr void resize(size_type n, const value_type& value)
374 requires std::is_copy_constructible_v<T>
375 {
376 if (n > N) {
377 throw_bad_alloc();
378 } else if (n < size_) {
379 std::destroy(data() + n, data() + size_);
380 set_size(n);
381 } else {
382 std::uninitialized_fill(data() + size_, data() + n, value);
383 set_size(size_ + n);
384 }
385 }
386
387 static constexpr void reserve(size_type n) {
388 if (n > N) {
389 throw_bad_alloc();
390 }
391 }
392 static constexpr void shrink_to_fit() noexcept {}
393
394 // element access
395
396 constexpr reference operator[](size_type pos) {
397 PLUGIFY_ASSERT(pos < size(), "index out of bounds");
398 return data()[pos];
399 }
400 constexpr reference front() {
401 PLUGIFY_ASSERT(!empty(), "called on an empty vector");
402 return data()[0];
403 }
404 constexpr reference back() {
405 PLUGIFY_ASSERT(!empty(), "called on an empty vector");
406 return data()[size_ - 1];
407 }
408
409 constexpr const_reference operator[](size_type pos) const {
410 PLUGIFY_ASSERT(pos < size(), "index out of bounds");
411 return data()[pos];
412 }
413 constexpr const_reference front() const {
414 PLUGIFY_ASSERT(!empty(), "called on an empty vector");
415 return data()[0];
416 }
417 constexpr const_reference back() const {
418 PLUGIFY_ASSERT(!empty(), "called on an empty vector");
419 return data()[size_ - 1];
420 }
421
422 constexpr reference at(size_type i) {
423 if (i >= size_) {
424 throw_out_of_range();
425 }
426 return data()[i];
427 }
428 constexpr const_reference at(size_type i) const {
429 if (i >= size_) {
430 throw_out_of_range();
431 }
432 return data()[i];
433 }
434
435 // [inplace.vector.data]
436
437 constexpr T* data() noexcept { return this->base_data(); }
438 constexpr const T* data() const noexcept { return this->base_data(); }
439 constexpr size_type size() const noexcept { return size_; }
440 static constexpr size_type max_size() noexcept { return N; }
441 static constexpr size_type capacity() noexcept { return N; }
442 [[nodiscard]] constexpr bool empty() const noexcept { return size_ == 0; };
443
444 // [inplace.vector.modifiers]
445
446 template<class... Args>
447 requires std::is_constructible_v<T, Args...>
448 value_type& unchecked_emplace_back(Args&&... args) {
449 // Precondition: (size_ < N)
450 value_type* p = data() + size_;
451 p = std::construct_at(p, std::forward<Args>(args)...);
452 set_size(size_ + 1);
453 return *p;
454 }
455 value_type& unchecked_push_back(const value_type& value)
456 requires std::is_copy_constructible_v<T>
457 {
458 return unchecked_emplace_back(value);
459 }
460 value_type& unchecked_push_back(value_type&& value)
461 requires std::is_move_constructible_v<T>
462 {
463 return unchecked_emplace_back(static_cast<value_type&&>(value));
464 }
465
466 template<class... Args>
467 requires std::is_constructible_v<T, Args...>
468 constexpr value_type* try_emplace_back(Args&&... args) {
469 if (size_ == N) {
470 return nullptr;
471 }
472 return std::addressof(unchecked_emplace_back(static_cast<Args&&>(args)...));
473 }
474 constexpr value_type* try_push_back(const value_type& value)
475 requires std::is_copy_constructible_v<T>
476 {
477 return try_emplace_back(value);
478 }
479 constexpr value_type* try_push_back(value_type&& value)
480 requires std::is_move_constructible_v<T>
481 {
482 return try_emplace_back(static_cast<value_type&&>(value));
483 }
484
485 template<class... Args>
486 requires std::is_constructible_v<T, Args...>
487 value_type& emplace_back(Args&&... args) {
488 if (size_ == N) {
489 throw_bad_alloc();
490 }
491 return unchecked_emplace_back(static_cast<Args&&>(args)...);
492 }
493 value_type& push_back(const value_type& value)
494 requires std::is_copy_constructible_v<T>
495 {
496 return emplace_back(value);
497 }
498 value_type& push_back(value_type&& value)
499 requires std::is_move_constructible_v<T>
500 {
501 return emplace_back(static_cast<value_type&&>(value));
502 }
503
504 #if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L
505 template<std::ranges::input_range R>
506 requires std::convertible_to<std::ranges::range_reference_t<R>, value_type>
507 constexpr void append_range(R&& rg) {
508 for (auto&& e : rg) {
509 emplace_back(static_cast<decltype(e)>(e));
510 }
511 }
512 #endif // __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L
513
514 void pop_back() {
515 std::destroy_at(data() + size_ - 1);
516 set_size(size_ - 1);
517 }
518
519 template<class... Args>
520 requires std::is_constructible_v<T, Args...>
521 iterator emplace(const_iterator pos, Args&&... args) {
522 auto it = iterator(pos);
523 emplace_back(static_cast<Args&&>(args)...);
524 std::rotate(it, end() - 1, end());
525 return it;
526 }
527 iterator insert(const_iterator pos, const value_type& value)
528 requires std::is_copy_constructible_v<T>
529 {
530 return emplace(pos, value);
531 }
532 iterator insert(const_iterator pos, value_type&& value)
533 requires std::is_move_constructible_v<T>
534 {
535 return emplace(pos, static_cast<value_type&&>(value));
536 }
537
538 iterator insert(const_iterator pos, size_type n, const value_type& value)
539 requires std::is_copy_constructible_v<T>
540 {
541 if (N - size_ < n) {
542 throw_bad_alloc();
543 }
544 auto it = iterator(pos);
545 auto oldend = end();
546 #if defined(__cpp_lib_trivially_relocatable)
547 // Open a window and fill in-place; if filling fails, close the window again.
548 if constexpr (std::is_trivially_relocatable_v<value_type>) {
549 std::uninitialized_relocate_backward(it, oldend, oldend + n);
550 try {
551 std::uninitialized_fill_n(it, n, value);
552 set_size(size_ + n);
553 } catch (...) {
554 std::uninitialized_relocate(it + n, oldend + n, it);
555 throw;
556 }
557 return it;
558 }
559 #endif
560 // Fill at the end of the vector, then rotate into place.
561 std::uninitialized_fill_n(oldend, n, value);
562 set_size(size_ + n);
563 std::rotate(it, oldend, oldend + n);
564 return it;
565 }
566
567 template<std::input_iterator InputIterator>
568 requires (std::is_constructible_v<T, typename std::iterator_traits<InputIterator>::value_type> && !std::is_const_v<T>)
569 iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
570 auto it = iterator(pos);
571 auto oldend = end();
572 if constexpr (std::random_access_iterator<InputIterator>) {
573 size_type n = static_cast<size_type>(std::distance(first, last));
574 if (N - size_ < n) {
575 throw_bad_alloc();
576 }
577 #if defined(__cpp_lib_trivially_relocatable)
578 // Open a window and fill in-place; if filling fails, close the window again.
579 if constexpr (std::is_trivially_relocatable_v<value_type>) {
580 std::uninitialized_relocate_backward(it, oldend, oldend + n);
581 try {
582 std::uninitialized_copy_n(first, n, it);
583 set_size(size_ + n);
584 } catch (...) {
585 std::uninitialized_relocate(it + n, oldend + n, it);
586 throw;
587 }
588 return it;
589 }
590 #endif
591 // Fill at the end of the vector, then rotate into place.
592 std::uninitialized_copy_n(first, n, oldend);
593 set_size(size_ + n);
594 std::rotate(it, oldend, oldend + n);
595 } else {
596 for (; first != last; ++first) {
597 emplace_back(*first);
598 }
599 std::rotate(it, oldend, end());
600 }
601 return it;
602 }
603
604 #if __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L
605 template<std::ranges::input_range R>
606 requires std::convertible_to<std::ranges::range_reference_t<R>, value_type>
607 iterator insert_range(const_iterator pos, R&& rg) {
608 auto it = iterator(pos);
609 auto oldend = end();
610 if constexpr (std::ranges::sized_range<R>) {
611 size_type n = std::ranges::size(rg);
612 if (N - size_ < n) {
613 throw_bad_alloc();
614 }
615 #if defined(__cpp_lib_trivially_relocatable)
616 // Open a window and fill in-place; if filling fails, close the window again.
617 if constexpr (std::is_trivially_relocatable_v<value_type>) {
618 std::uninitialized_relocate_backward(it, oldend, oldend + n);
619 try {
620 std::ranges::uninitialized_copy_n(std::ranges::begin(rg), n, it, std::unreachable_sentinel);
621 set_size(size_ + n);
622 } catch (...) {
623 std::uninitialized_relocate(it + n, oldend + n, it);
624 throw;
625 }
626 return it;
627 }
628 #endif
629 // Fill at the end of the vector, then rotate into place.
630 std::ranges::uninitialized_copy_n(std::ranges::begin(rg), n, oldend, std::unreachable_sentinel);
631 set_size(size_ + n);
632 std::rotate(it, oldend, oldend + n);
633 } else {
634 auto [rgend, newend] = std::ranges::uninitialized_copy(rg, std::ranges::subrange(oldend, data() + N));
635 if (rgend != std::ranges::end(rg)) {
636 std::destroy(oldend, newend);
637 throw_bad_alloc();
638 } else {
639 set_size(newend - data());
640 std::rotate(it, oldend, newend);
641 }
642 }
643 return it;
644 }
645 #endif // __cpp_lib_ranges >= 201911L && __cpp_lib_ranges_to_container >= 202202L
646
647 iterator insert(const_iterator pos, std::initializer_list<value_type> il)
648 requires (std::is_copy_constructible_v<T> && !std::is_const_v<T>)
649 {
650 return insert(pos, il.begin(), il.end());
651 }
652
653 iterator erase(const_iterator pos)
654 requires (!std::is_const_v<T>)
655 {
656 auto it = iterator(pos);
657 auto oldend = end();
658 #if defined(__cpp_lib_trivially_relocatable)
659 if constexpr (std::is_trivially_relocatable_v<value_type>) {
660 std::destroy_at(it);
661 std::uninitialized_relocate(it + 1, oldend, it);
662 set_size(size_ - 1);
663 return it;
664 }
665 #endif
666 std::move(it + 1, oldend, it);
667 std::destroy_at(oldend - 1);
668 set_size(size_ - 1);
669 return it;
670 }
671
672 iterator erase(const_iterator first, const_iterator last)
673 requires (!std::is_const_v<T>)
674 {
675 auto ifirst = iterator(first);
676 auto ilast = iterator(last);
677 auto n = static_cast<size_type>(std::distance(ifirst, ilast));
678 if (n != 0) {
679 auto oldend = end();
680 #if defined(__cpp_lib_trivially_relocatable)
681 if constexpr (std::is_trivially_relocatable_v<value_type>) {
682 std::destroy(ifirst, ilast);
683 std::uninitialized_relocate(ilast, oldend, ifirst);
684 set_size(size_ - n);
685 return ifirst;
686 }
687 #endif // __cpp_lib_trivially_relocatable
688 std::destroy(std::move(ilast, oldend, ifirst), oldend);
689 set_size(size_ - n);
690 }
691 return ifirst;
692 }
693
694 constexpr void clear() noexcept {
695 std::destroy(data(), data() + size_);
696 set_size(0);
697 }
698
699 constexpr void swap(inplace_vector& b)
700 noexcept(N == 0 || (std::is_nothrow_swappable_v<T> && std::is_nothrow_move_constructible_v<T>))
701 requires (!std::is_const_v<T>)
702 {
703 auto& a = *this;
704 if (a.size_ < b.size_) {
705 b.swap(a);
706 } else {
707 std::swap_ranges(a.data(), a.data() + b.size_, b.data());
708 #if defined(__cpp_lib_trivially_relocatable)
709 size_t n = a.size_;
710 a.set_size(b.size_);
711 std::uninitialized_relocate(a.data() + b.size_, a.data() + n, b.data() + b.size_);
712 b.set_size(n);
713 #else
714 std::uninitialized_move(a.data() + b.size_, a.data() + a.size_, b.data() + b.size_);
715 std::destroy(a.data() + b.size_, a.data() + a.size_);
716 if constexpr (N != 0) {
717 std::swap(a.size_, b.size_);
718 }
719 #endif
720 }
721 }
722
723 friend constexpr void swap(inplace_vector& a, inplace_vector& b) noexcept(noexcept(a.swap(b))) {
724 a.swap(b);
725 }
726
727 constexpr friend bool operator==(const inplace_vector& lhs, const inplace_vector& rhs) {
728 if (lhs.size() != rhs.size()) return false;
729 return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
730 }
731
732 #if __cpp_impl_three_way_comparison >= 201907L
733 constexpr friend auto operator<=>(const inplace_vector& lhs, const inplace_vector& rhs) {
734 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
735 }
736 #else
737 constexpr friend bool operator<(const inplace_vector& a, const inplace_vector& b) {
738 const T* adata = a.data();
739 const T* bdata = b.data();
740 size_t n = (a.size_ < b.size_) ? a.size_ : b.size_;
741 for (size_t i = 0; i < n; ++i) {
742 if (adata[i] < bdata[i]) {
743 return true;
744 } else if (bdata[i] < adata[i]) {
745 return false;
746 }
747 }
748 return (a.size_ < b.size_);
749 }
750 constexpr friend bool operator>(const inplace_vector& a, const inplace_vector& b) { return (b < a); }
751 constexpr friend bool operator<=(const inplace_vector& a, const inplace_vector& b) { return !(b < a); }
752 constexpr friend bool operator>=(const inplace_vector& a, const inplace_vector& b) { return !(a < b); }
753 constexpr friend bool operator!=(const inplace_vector& a, const inplace_vector& b) { return !(a == b); }
754 #endif
755
756 private:
757 [[noreturn]] static void throw_bad_alloc() {
758 PLUGIFY_THROW("memory size would exceed capacity()", std::bad_alloc);
759 }
760
761 [[noreturn]] static void throw_out_of_range() {
762 PLUGIFY_THROW("input index is out of bounds", std::out_of_range);
763 }
764 };
765} // namespace plg
766
767namespace std {
768 template<class T, std::size_t N>
769 using inplace_vector = plg::inplace_vector<T, N>;
770} // namespace std
771
772#endif