[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