[libcxx-commits] [libcxx] [libc++] Implement P0843R14: `inplace_vector` (PR #204008)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jun 15 18:56:26 PDT 2026
================
@@ -0,0 +1,1132 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_INPLACE_VECTOR
+#define _LIBCPP_INPLACE_VECTOR
+
+/*
+ inplace_vector synopsis
+
+namespace std {
+ template<class T, size_t N>
+ class inplace_vector {
+ public:
+ // types:
+ using value_type = T;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using iterator = implementation-defined; // see [container.requirements]
+ using const_iterator = implementation-defined; // see [container.requirements]
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // [inplace.vector.cons], construct/copy/destroy
+ constexpr inplace_vector() noexcept;
+ constexpr explicit inplace_vector(size_type n); // freestanding-deleted
+ constexpr inplace_vector(size_type n, const T& value); // freestanding-deleted
+ template<class InputIterator>
+ constexpr inplace_vector(InputIterator first, InputIterator last); // freestanding-deleted
+ template<container-compatible-range<T> R>
+ constexpr inplace_vector(from_range_t, R&& rg); // freestanding-deleted
+ constexpr inplace_vector(const inplace_vector&);
+ constexpr inplace_vector(inplace_vector&&)
+ noexcept(N == 0 || is_nothrow_move_constructible_v<T>);
+ constexpr inplace_vector(initializer_list<T> il); // freestanding-deleted
+ constexpr ~inplace_vector();
+ constexpr inplace_vector& operator=(const inplace_vector& other);
+ constexpr inplace_vector& operator=(inplace_vector&& other)
+ noexcept(N == 0 || (is_nothrow_move_assignable_v<T> &&
+ is_nothrow_move_constructible_v<T>));
+ constexpr inplace_vector& operator=(initializer_list<T>); // freestanding-deleted
+ template<class InputIterator>
+ constexpr void assign(InputIterator first, InputIterator last); // freestanding-deleted
+ template<container-compatible-range<T> R>
+ constexpr void assign_range(R&& rg); // freestanding-deleted
+ constexpr void assign(size_type n, const T& u); // freestanding-deleted
+ constexpr void assign(initializer_list<T> il); // freestanding-deleted
+
+ // iterators
+ constexpr iterator begin() noexcept;
+ constexpr const_iterator begin() const noexcept;
+ constexpr iterator end() noexcept;
+ constexpr const_iterator end() const noexcept;
+ constexpr reverse_iterator rbegin() noexcept;
+ constexpr const_reverse_iterator rbegin() const noexcept;
+ constexpr reverse_iterator rend() noexcept;
+ constexpr const_reverse_iterator rend() const noexcept;
+
+ constexpr const_iterator cbegin() const noexcept;
+ constexpr const_iterator cend() const noexcept;
+ constexpr const_reverse_iterator crbegin() const noexcept;
+ constexpr const_reverse_iterator crend() const noexcept;
+
+ // [inplace.vector.capacity], capacity
+ constexpr bool empty() const noexcept;
+ constexpr size_type size() const noexcept;
+ static constexpr size_type max_size() noexcept;
+ static constexpr size_type capacity() noexcept;
+ constexpr void resize(size_type sz); // freestanding-deleted
+ constexpr void resize(size_type sz, const T& c); // freestanding-deleted
+ static constexpr void reserve(size_type n); // freestanding-deleted
+ static constexpr void shrink_to_fit() noexcept;
+
+ // element access
+ constexpr reference operator[](size_type n);
+ constexpr const_reference operator[](size_type n) const;
+ constexpr reference at(size_type n); // freestanding-deleted
+ constexpr const_reference at(size_type n) const; // freestanding-deleted
+ constexpr reference front();
+ constexpr const_reference front() const;
+ constexpr reference back();
+ constexpr const_reference back() const;
+
+ // [inplace.vector.data], data access
+ constexpr T* data() noexcept;
+ constexpr const T* data() const noexcept;
+
+ // [inplace.vector.modifiers], modifiers
+ template<class... Args>
+ constexpr reference emplace_back(Args&&... args); // freestanding-deleted
+ constexpr reference push_back(const T& x); // freestanding-deleted
+ constexpr reference push_back(T&& x); // freestanding-deleted
+ template<container-compatible-range<T> R>
+ constexpr void append_range(R&& rg); // freestanding-deleted
+ constexpr void pop_back();
+
+ template<class... Args>
+ constexpr optional<reference> try_emplace_back(Args&&... args);
+ constexpr optional<reference> try_push_back(const T& x);
+ constexpr optional<reference> try_push_back(T&& x);
+
+ template<class... Args>
+ constexpr reference unchecked_emplace_back(Args&&... args);
+ constexpr reference unchecked_push_back(const T& x);
+ constexpr reference unchecked_push_back(T&& x);
+
+ template<class... Args>
+ constexpr iterator emplace(const_iterator position, Args&&... args); // freestanding-deleted
+ constexpr iterator insert(const_iterator position, const T& x); // freestanding-deleted
+ constexpr iterator insert(const_iterator position, T&& x); // freestanding-deleted
+ constexpr iterator insert(const_iterator position, size_type n, // freestanding-deleted
+ const T& x);
+ template<class InputIterator>
+ constexpr iterator insert(const_iterator position, // freestanding-deleted
+ InputIterator first, InputIterator last);
+ template<container-compatible-range<T> R>
+ constexpr iterator insert_range(const_iterator position, R&& rg); // freestanding-deleted
+ constexpr iterator insert(const_iterator position, // freestanding-deleted
+ initializer_list<T> il);
+ constexpr iterator erase(const_iterator position);
+ constexpr iterator erase(const_iterator first, const_iterator last);
+ constexpr void swap(inplace_vector& x)
+ noexcept(N == 0 || (is_nothrow_swappable_v<T> &&
+ is_nothrow_move_constructible_v<T>));
+ constexpr void clear() noexcept;
+
+ friend constexpr bool operator==(const inplace_vector& x,
+ const inplace_vector& y);
+ friend constexpr auto
+ operator<=>(const inplace_vector& x, const inplace_vector& y)
+ requires requires (const T t) { synth-three-way(t, t); }
+ {
+ return lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end(),
+ synth-three-way);
+ }
+ friend constexpr void swap(inplace_vector& x, inplace_vector& y)
+ noexcept(N == 0 || (is_nothrow_swappable_v<T> &&
+ is_nothrow_move_constructible_v<T>))
+ { x.swap(y); }
+ };
+}
+*/
+
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+# include <__cxx03/__config>
+#else
+# include <__config>
+
+# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+# endif
+
+_LIBCPP_PUSH_MACROS
+# include <__undef_macros>
+
+# if _LIBCPP_STD_VER >= 26
+# include <__algorithm/fill_n.h>
+# include <__algorithm/lexicographical_compare_three_way.h>
+# include <__algorithm/min.h>
+# include <__algorithm/move.h>
+# include <__algorithm/move_backward.h>
+# include <__algorithm/ranges_copy.h>
+# include <__algorithm/ranges_copy_n.h>
+# include <__algorithm/remove.h>
+# include <__algorithm/remove_if.h>
+# include <__algorithm/rotate.h>
+# include <__algorithm/swap_ranges.h>
+# include <__compare/synth_three_way.h>
+# include <__compare/three_way_comparable.h>
+# include <__concepts/assignable.h>
+# include <__cstddef/ptrdiff_t.h>
+# include <__cstddef/size_t.h>
+# include <__iterator/bounded_iter.h>
+# include <__iterator/capacity_aware_iterator.h>
+# include <__iterator/concepts.h>
+# include <__iterator/distance.h>
+# include <__iterator/iterator_traits.h>
+# include <__iterator/move_iterator.h>
+# include <__iterator/reverse_iterator.h>
+# include <__memory/construct_at.h>
+# include <__memory/destroy.h>
+# include <__memory/ranges_destroy.h>
+# include <__memory/ranges_uninitialized_algorithms.h>
+# include <__memory/uninitialized_algorithms.h>
+# include <__new/exceptions.h>
+# include <__ranges/access.h>
+# include <__ranges/concepts.h>
+# include <__ranges/container_compatible_range.h>
+# include <__ranges/from_range.h>
+# include <__type_traits/is_nothrow_constructible.h>
+# include <__type_traits/is_swappable.h>
+# include <__type_traits/is_trivially_assignable.h>
+# include <__type_traits/is_trivially_constructible.h>
+# include <__type_traits/is_trivially_copyable.h>
+# include <__type_traits/is_trivially_destructible.h>
+# include <__utility/exception_guard.h>
+# include <__utility/move.h>
+# include <__utility/unreachable.h>
+# include <__verbose_trap>
+# include <initializer_list>
+# include <limits>
+# include <optional>
+# include <stdexcept>
+# include <version>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Tp, size_t _Cap>
+class inplace_vector {
+private:
+ static constexpr decltype(auto) __get_size_type() {
+ if constexpr (_Cap <= std::numeric_limits<unsigned char>::max() && sizeof(unsigned char) < sizeof(std::size_t))
+ return static_cast<unsigned char>(0);
+ else if constexpr (_Cap <= std::numeric_limits<unsigned short>::max() &&
+ sizeof(unsigned short) < sizeof(std::size_t))
+ return static_cast<unsigned short>(0);
+ else if constexpr (_Cap <= std::numeric_limits<unsigned int>::max() && sizeof(unsigned int) < sizeof(std::size_t))
+ return static_cast<unsigned int>(0);
+ else if constexpr (_Cap <= std::numeric_limits<unsigned long>::max() && sizeof(unsigned long) < sizeof(std::size_t))
+ return static_cast<unsigned long>(0);
+ else
+ return static_cast<std::size_t>(0);
+ }
+
+ using __size_type = decltype(__get_size_type());
+
+ union {
+ _Tp __elems_[_Cap];
+ };
+ __size_type __size_{0};
+
+ constexpr void __init() {
+ if consteval {
+ if constexpr (is_trivially_copyable_v<_Tp> && is_trivially_default_constructible_v<_Tp> &&
+ is_trivially_assignable_v<_Tp&, _Tp>) {
+ for (size_t __i{0}; __i < _Cap; ++__i) {
+ __elems_[__i] = _Tp();
+ }
+ } else {
+# if !defined(__cpp_trivial_union)
+ // TODO: Clang emits a -Wformat-security warning for this when it's in a consteval block. File bug for this
+ _LIBCPP_VERBOSE_ABORT("%s",
+ "std::inplace_vector<T,N> currently cannot be used in constant expressions for "
+ "non-trivial T on compilers where P3074 is not implemented.");
+# endif
+ }
+ }
+ // std::start_lifetime_as_array?
+ }
+
+public:
+ using value_type = _Tp;
+ using pointer = _Tp*;
+ using const_pointer = const _Tp*;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_INPLACE_VECTOR
+ using iterator = __bounded_iter<pointer>;
+ using const_iterator = __bounded_iter<const_pointer>;
+# else
+ using iterator = __capacity_aware_iterator<pointer, inplace_vector<_Tp, _Cap>, _Cap>;
+ using const_iterator = __capacity_aware_iterator<const_pointer, inplace_vector<_Tp, _Cap>, _Cap>;
+# endif
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+private:
+ struct _ConstructTransaction {
+ constexpr explicit _ConstructTransaction(inplace_vector& __v, size_type __n)
+ : __v_(__v), __pos_(__v.data() + __v.__size_), __new_end_(__v.data() + __v.__size_ + __n) {}
+
+ constexpr ~_ConstructTransaction() { __v_.__size_ = __pos_ - __v_.data(); }
+
+ inplace_vector& __v_;
+ pointer __pos_;
+ const_pointer const __new_end_;
+
+ _ConstructTransaction(_ConstructTransaction const&) = delete;
+ _ConstructTransaction& operator=(_ConstructTransaction const&) = delete;
+ };
+
+ constexpr void __move_range(pointer __from_s, pointer __from_e, pointer __to) {
+ pointer __old_last = data() + __size_;
+ difference_type __n = __old_last - __to;
+ {
+ pointer __i = __from_s + __n;
+ _ConstructTransaction __tx(*this, __from_e - __i);
+ for (pointer __pos = __tx.__pos_; __i < __from_e; ++__i, ++__pos, __tx.__pos_ = __pos) {
+ std::construct_at(__pos, std::move(*__i));
+ }
+ }
+ std::move_backward(__from_s, __from_s + __n, __old_last);
+ }
+
+ template <class _It, class _Sentinel>
+ constexpr void __assign(_It __first, _Sentinel __last) {
+ if constexpr (std::sized_sentinel_for<_Sentinel, _It>) {
+ auto __d = std::ranges::distance(__first, __last);
+ reserve(__d);
+
+ if (size() > static_cast<size_type>(__d)) {
+ auto [__it_in, __it_out] = std::ranges::copy(std::move(__first), __last, begin());
+ std::ranges::destroy(__it_out, end());
+ } else {
+ auto [__it_in, __it_out] = std::ranges::copy_n(std::move(__first), __size_, data());
+ std::ranges::uninitialized_copy(__it_in, __last, __it_out, data() + _Cap);
+ }
+ __size_ = __d;
+ } else {
+ size_t __d = 0;
+ for (; __first != __last && __d < __size_; (void)++__first, ++__d) {
+ data()[__d] = *__first;
+ }
+
+ if (__first == __last) { // __d <= size()
+ std::destroy(data() + __d, data() + __size_);
+ __size_ = __d;
+ } else { // __d > size()
+ while (__first != __last) {
+ emplace_back(*__first);
+ __first++;
+ }
+ }
+ }
+ }
+
+ template <class _It, class _Sentinel>
+ constexpr iterator __insert(const_iterator __position, _It __first, _Sentinel __last) {
+ pointer __pos = data() + (__position - cbegin());
+ size_type __old_size = __size_;
+ if constexpr (std::sized_sentinel_for<_Sentinel, _It>) {
+ const auto __d = std::ranges::distance(__first, __last);
+ if (__d > static_cast<difference_type>(_Cap - __size_)) {
+ std::__throw_bad_alloc();
+ }
+ std::ranges::uninitialized_copy(std::move(__first), __last, data() + __size_, data() + _Cap);
+ __size_ += __d;
+ } else {
+ auto __guard = std::__make_exception_guard([this, __old_size]() {
+ std::ranges::destroy(this->data() + __old_size, this->data() + __size_);
+ this->__size_ = __old_size;
+ });
+
+ while (__first != __last) {
+ emplace_back(*__first);
+ __first++;
+ }
+
+ __guard.__complete();
+ }
+
+ iterator __iter = __make_iter(__pos);
+ if (__size_ > 0) {
+ std::rotate(__iter, begin() + __old_size, end());
+ }
+
+ return __iter;
+ }
+
+ constexpr iterator __make_iter(pointer __p) {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_INPLACE_VECTOR
+ return std::__make_bounded_iter(__p, data(), data() + __size_);
+# else
+ return std::__make_capacity_aware_iterator<pointer, inplace_vector<_Tp, _Cap>, _Cap>(__p);
+# endif
+ }
+
+ constexpr const_iterator __make_iter(const_pointer __p) const {
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_INPLACE_VECTOR
+ return std::__make_bounded_iter(__p, data(), data() + __size_);
+# else
+ return std::__make_capacity_aware_iterator<const_pointer, inplace_vector<_Tp, _Cap>, _Cap>(__p);
+# endif
+ }
+
+public:
+ // [inplace.vector.cons], construct/copy/destroy
+ constexpr inplace_vector() noexcept { __init(); }
+
+ constexpr explicit inplace_vector(size_type __n) {
+ __init();
+ reserve(__n);
+ std::uninitialized_value_construct_n(data(), __n);
+ __size_ = __n;
+ }
+
+ constexpr inplace_vector(size_type __n, const _Tp& __c) {
+ __init();
+ reserve(__n);
+ std::uninitialized_fill_n(data(), __n, __c);
+ __size_ = __n;
+ }
+
+ template <class _InputIterator>
+ requires __has_input_iterator_category<_InputIterator>::value
+ constexpr inplace_vector(_InputIterator __first, _InputIterator __last) {
+ __init();
+ if constexpr (std::sized_sentinel_for<_InputIterator, _InputIterator>) {
+ auto __n = std::ranges::distance(__first, __last);
+ reserve(__n);
+ std::uninitialized_copy(__first, __last, data());
+ __size_ = __n;
+ } else {
+ auto __guard = std::__make_exception_guard([this] { clear(); });
+
+ while (__first != __last) {
+ emplace_back(*__first);
+ __first++;
+ }
+
+ __guard.__complete();
+ }
+ }
+
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ constexpr inplace_vector(from_range_t, _Range&& __rg) {
+ __init();
+ append_range(__rg);
+ }
+
+ constexpr inplace_vector(const inplace_vector&)
+ requires is_trivially_copy_constructible_v<_Tp>
+ = default;
+
+ constexpr inplace_vector(const inplace_vector& __x) noexcept(is_nothrow_copy_constructible_v<_Tp>) {
+ __init();
+ std::uninitialized_copy(__x.begin(), __x.end(), data());
+ __size_ = __x.size();
+ }
+
+ constexpr inplace_vector(inplace_vector&&)
+ requires is_trivially_move_constructible_v<_Tp>
+ = default;
+
+ constexpr inplace_vector(inplace_vector&& __x) noexcept(is_nothrow_move_constructible_v<_Tp>) {
+ __init();
+ std::uninitialized_move(__x.begin(), __x.end(), data());
+ __size_ = __x.size();
+ }
+
+ constexpr inplace_vector(std::initializer_list<_Tp> __il) {
+ __init();
+ reserve(__il.size());
+ std::uninitialized_copy(__il.begin(), __il.end(), data());
+ __size_ = __il.size();
+ }
+
+ constexpr ~inplace_vector()
+ requires is_trivially_destructible_v<_Tp>
+ = default;
+
+ constexpr ~inplace_vector() { clear(); }
+
+ constexpr inplace_vector& operator=(const inplace_vector&)
+ requires is_trivially_destructible_v<_Tp> && is_trivially_copy_assignable_v<_Tp> &&
+ is_trivially_copy_constructible_v<_Tp>
+ = default;
+
+ constexpr inplace_vector& operator=(const inplace_vector& __x) noexcept(
+ is_nothrow_copy_constructible_v<_Tp> && is_nothrow_copy_assignable_v<_Tp>) {
+ if (std::addressof(__x) != this) {
+ assign(__x.begin(), __x.end());
+ }
+
+ return *this;
+ }
+
+ constexpr inplace_vector& operator=(inplace_vector&&)
+ requires is_trivially_destructible_v<_Tp> && is_trivially_move_assignable_v<_Tp> &&
+ is_trivially_move_constructible_v<_Tp>
+ = default;
+
+ constexpr inplace_vector&
+ operator=(inplace_vector&& __x) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_move_assignable_v<_Tp>) {
+ if (std::addressof(__x) != this) {
+ assign(std::make_move_iterator(__x.begin()), std::make_move_iterator(__x.end()));
+ }
+
+ return *this;
+ }
+
+ constexpr inplace_vector& operator=(std::initializer_list<_Tp> __il) {
+ assign(__il.begin(), __il.end());
+ return *this;
+ }
+
+ template <class _InputIterator>
+ requires __has_input_iterator_category<_InputIterator>::value
+ constexpr void assign(_InputIterator __first, _InputIterator __last) {
+ __assign(__first, __last);
+ }
+
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ constexpr void assign_range(_Range&& __rg) {
+ static_assert(std::assignable_from<_Tp&, std::ranges::range_reference_t<_Range>>);
+ __assign(std::ranges::begin(__rg), std::ranges::end(__rg));
+ }
+
+ constexpr void assign(size_type __n, const _Tp& __u) {
+ reserve(__n);
+ std::__fill_n(begin(), std::min(__n, static_cast<size_type>(__size_)), __u);
+
+ if (__n <= size()) {
+ std::destroy(begin() + __n, end());
+ } else {
+ std::uninitialized_fill(data() + size(), data() + __n, __u);
+ }
+ __size_ = __n;
+ }
+
+ constexpr void assign(std::initializer_list<_Tp> __il) { assign(__il.begin(), __il.end()); }
+
+ // iterators
+ [[nodiscard]] constexpr iterator begin() noexcept { return __make_iter(data()); }
+ [[nodiscard]] constexpr const_iterator begin() const noexcept { return __make_iter(data()); }
+ [[nodiscard]] constexpr iterator end() noexcept { return __make_iter(data() + __size_); }
+ [[nodiscard]] constexpr const_iterator end() const noexcept { return __make_iter(data() + __size_); }
+
+ [[nodiscard]] constexpr reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
+ [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
+ [[nodiscard]] constexpr reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
+ [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
+
+ [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
+ [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
+ [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+ [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
+
+ // [inplace.vector.capacity], capacity
+ [[nodiscard]] constexpr bool empty() const noexcept { return __size_ == 0; }
+ [[nodiscard]] constexpr size_type size() const noexcept { return __size_; }
+ [[nodiscard]] static constexpr size_type max_size() noexcept { return _Cap; }
+ [[nodiscard]] static constexpr size_type capacity() noexcept { return _Cap; }
+
+ constexpr void resize(size_t __count) {
+ reserve(__count);
+ if (__count > __size_) {
+ std::uninitialized_value_construct_n(data() + __size_, __count - __size_);
+ } else if (__count < __size_) {
+ std::destroy_n(data() + __count, __size_ - __count);
+ }
+ __size_ = __count;
+ }
+
+ constexpr void resize(size_t __count, const _Tp& __v) {
+ reserve(__count);
+ if (__count > __size_) {
+ std::uninitialized_fill_n(data() + __size_, __count - __size_, __v);
+ } else if (__count < __size_) {
+ std::destroy_n(data() + __count, __size_ - __count);
+ }
+ __size_ = __count;
+ }
+
+ static constexpr void reserve(size_type __new_cap) {
+ if (__new_cap > _Cap) {
+ std::__throw_bad_alloc();
+ }
+ }
+
+ static constexpr void shrink_to_fit() noexcept {}
+
+ // element access
+ [[nodiscard]] constexpr reference operator[](size_type __pos) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __pos < _Cap, "inplace_vector<T, N>::operator[](size_type pos): index with pos >= N");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __pos < __size_, "inplace_vector<T, N>::operator[](size_type pos): index with pos >= size()");
+ return data()[__pos];
+ }
+
+ [[nodiscard]] constexpr const_reference operator[](size_type __pos) const {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __pos < _Cap, "inplace_vector<T, N>::operator[](size_type pos) const: index with pos >= N");
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ __pos < __size_, "inplace_vector<T, N>::operator[](size_type pos) const: index with pos >= size()");
+ return data()[__pos];
+ }
+
+ [[nodiscard]] constexpr reference at(size_type __pos) {
+ if (__pos >= __size_) {
+ std::__throw_out_of_range("inplace_vector<T,N>::at(size_type pos): access with pos >= size()");
+ }
+ return data()[__pos];
+ }
+
+ [[nodiscard]] constexpr const_reference at(size_type __pos) const {
+ if (__pos >= __size_) {
+ std::__throw_out_of_range("inplace_vector<T,N>::at(size_type pos) const: access with pos >= size()");
+ }
+ return data()[__pos];
+ }
+
+ [[nodiscard]] constexpr reference front() {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__size_ > 0, "inplace_vector<T,N>::front(): access with size() == 0");
+ return *data();
+ }
+
+ [[nodiscard]] constexpr const_reference front() const {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__size_ > 0, "inplace_vector<T,N>::front() const: access with size() == 0");
+ return *data();
+ }
+
+ [[nodiscard]] constexpr reference back() {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__size_ > 0, "inplace_vector<T,N>::back(): access with size() == 0");
+ return *(data() + __size_ - 1);
+ }
+
+ [[nodiscard]] constexpr const_reference back() const {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__size_ > 0, "inplace_vector<T,N>::back() const: access with size() == 0");
+ return *(data() + __size_ - 1);
+ }
+
+ // [inplace.vector.data], data access
+ [[nodiscard]] constexpr pointer data() noexcept { return __elems_; }
+ [[nodiscard]] constexpr const_pointer data() const noexcept { return __elems_; }
+
+ // [inplace.vector.modifiers], modifiers
+ template <class... _Args>
+ constexpr reference emplace_back(_Args&&... __args) {
+ if (size() == capacity())
+ std::__throw_bad_alloc();
+ return unchecked_emplace_back(std::forward<_Args>(__args)...);
+ }
+
+ constexpr reference push_back(const _Tp& __x) { return emplace_back(__x); }
+ constexpr reference push_back(_Tp&& __x) { return emplace_back(std::move(__x)); }
+
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ constexpr void append_range(_Range&& __rg) {
+ if constexpr (std::ranges::sized_range<_Range> || std::ranges::forward_range<_Range>) {
+ const auto __d = std::ranges::distance(__rg);
+
+ if (__d > static_cast<difference_type>(_Cap - size())) {
+ std::__throw_bad_alloc();
+ }
+
+ std::ranges::uninitialized_copy(
+ std::ranges::begin(__rg), std::ranges::end(__rg), data() + __size_, data() + __size_ + __d);
+ __size_ += __d;
+ } else {
+ auto __guard = std::__make_exception_guard([__old_size = this->__size_, this] {
+ std::destroy(this->data() + __old_size, this->data() + this->__size_);
+ this->__size_ = __old_size;
+ });
+
+ auto [__it_in, __it_out] = std::ranges::uninitialized_copy(
+ std::ranges::begin(__rg), std::ranges::end(__rg), data() + __size_, data() + _Cap);
+
+ __size_ = __it_out - data();
+
+ if (__it_in != std::ranges::end(__rg)) {
+ std::__throw_bad_alloc();
+ }
+
+ __guard.__complete();
+ }
+ }
+
+ constexpr void pop_back() {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "inplace_vector<T,N>::pop_back(): Called on empty inplace_vector");
+ --__size_;
+ std::destroy_at(data() + __size_);
+ }
+
+ template <class... _Args>
+ [[nodiscard]] constexpr optional<reference> try_emplace_back(_Args&&... __args) {
+ if (size() == capacity()) {
+ return std::nullopt;
+ }
+
+ return std::optional<reference>(std::in_place, unchecked_emplace_back(std::forward<_Args>(__args)...));
+ }
+
+ [[nodiscard]] constexpr optional<reference> try_push_back(const _Tp& __x) { return try_emplace_back(__x); }
+ [[nodiscard]] constexpr optional<reference> try_push_back(_Tp&& __x) { return try_emplace_back(std::move(__x)); }
+
+ template <class... _Args>
+ constexpr reference unchecked_emplace_back(_Args&&... __args) {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+ size() < capacity(),
+ "inplace_vector<T,N>::unchecked_emplace_back(Args...): Adding element when size() >= capacity()");
+ {
+ _ConstructTransaction __tx(*this, 1);
+ std::construct_at(data() + __size_, std::forward<_Args>(__args)...);
+ __tx.__pos_++;
+ }
+
+ return back();
+ }
+
+ constexpr reference unchecked_push_back(const _Tp& __x) { return unchecked_emplace_back(__x); }
+ constexpr reference unchecked_push_back(_Tp&& __x) { return unchecked_emplace_back(std::move(__x)); }
+
+ template <class... _Args>
+ constexpr iterator emplace(const_iterator __position, _Args&&... __args) {
+ if (size() == capacity()) {
+ std::__throw_bad_alloc();
+ }
+
+ pointer __p = data() + (__position - begin());
+
+ if (__position == end()) {
+ emplace_back(std::forward<_Args>(__args)...);
+ } else {
+ _Tp __temp_value = _Tp(std::forward<_Args>(__args)...);
+ __move_range(__p, data() + __size_, __p + 1);
+ *__p = std::move(__temp_value);
+ }
+
+ return __make_iter(__p);
+ }
+
+ constexpr iterator insert(const_iterator __position, const _Tp& __x) { return emplace(__position, __x); }
+ constexpr iterator insert(const_iterator __position, _Tp&& __x) { return emplace(__position, std::move(__x)); }
+
+ constexpr iterator insert(const_iterator __position, size_type __n, const _Tp& __x) {
+ if ((_Cap - __size_) < __n) {
+ std::__throw_bad_alloc();
+ }
+
+ pointer __p = data() + (__position - begin());
+
+ if (__n == 0) {
+ return __make_iter(__p);
+ }
+
+ std::uninitialized_fill_n(data() + __size_, __n, __x);
+ __size_ = __size_ + __n;
+
+ iterator __it = __make_iter(__p);
+
+ std::rotate(__it, end() - __n, end());
+
+ return __it;
+ }
+
+ template <class _InputIterator>
+ requires __has_input_iterator_category<_InputIterator>::value
+ constexpr iterator insert(const_iterator __position, _InputIterator __first, _InputIterator __last) {
+ return __insert(__position, __first, __last);
+ }
+
+ template <_ContainerCompatibleRange<_Tp> _Range>
+ constexpr iterator insert_range(const_iterator __position, _Range&& __rg) {
+ return __insert(__position, std::ranges::begin(__rg), std::ranges::end(__rg));
+ }
+
+ constexpr iterator insert(const_iterator __position, std::initializer_list<_Tp> __il) {
+ return __insert(__position, __il.begin(), __il.end());
+ }
+
+ constexpr iterator erase(const_iterator __position) {
+ pointer __pos = data() + (__position - cbegin());
+
+ std::move(__pos + 1, data() + __size_, __pos);
+ pop_back();
+
+ return __make_iter(__pos);
+ }
+
+ constexpr iterator erase(const_iterator __first, const_iterator __last) {
+ __size_type __x = __last - __first;
+ pointer __pos = data() + (__first - cbegin());
+
+ if (__x == 0) {
+ return __make_iter(__pos);
+ }
+
+ pointer __end = std::move(__pos + __x, data() + __size_, __pos);
+ std::destroy_n(__end, __x);
+
+ __size_ -= __x;
+ return __make_iter(__pos);
+ }
+
+ constexpr void
+ swap(inplace_vector& __x) noexcept(is_nothrow_swappable_v<_Tp> && is_nothrow_move_constructible_v<_Tp>) {
+ inplace_vector& __smaller = __x.size() < size() ? __x : *this;
+ inplace_vector& __bigger = __x.size() >= size() ? __x : *this;
+
+ size_type __n1 = __smaller.size();
+ size_type __n2 = __bigger.size();
+
+ std::swap_ranges(__smaller.begin(), __smaller.end(), __bigger.begin());
+
+ if (__n1 != __n2) {
+ using std::swap;
+ std::uninitialized_move(__bigger.data() + __n1, __bigger.data() + __n2, __smaller.data() + __n1);
+ __smaller.__size_ = __n2;
+ std::destroy(__bigger.data() + __n1, __bigger.data() + __n2);
+ __bigger.__size_ = __n1;
+ }
+ }
+
+ constexpr void clear() noexcept {
+ std::destroy(begin(), end());
+ __size_ = 0;
+ }
+
+ friend constexpr bool operator==(const inplace_vector& __x, const inplace_vector& __y) {
+ if (__x.size() != __y.size()) {
+ return false;
+ }
+
+ for (size_type __i{0}; __i < __x.size(); __i++) {
+ if (__x[__i] != __y[__i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ friend constexpr auto operator<=>(const inplace_vector& __x, const inplace_vector& __y)
+ requires requires(const _Tp __t) { std::__synth_three_way(__t, __t); }
----------------
frederick-vs-ja wrote:
This implements [LWG4122](https://cplusplus.github.io/LWG/issue4122), and thus we should also update the corresponding row in `libcxx/docs/Status/Cxx2cIssues.csv` and resolve #189816.
https://github.com/llvm/llvm-project/pull/204008
More information about the libcxx-commits
mailing list