[libcxx-commits] [libcxx] [libc++] Implement P0843R14: `inplace_vector` (PR #204008)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jun 18 01:51:14 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) {
----------------
philnik777 wrote:
We actually have tests that would complain about this, because users might write `assign(iter, {})`.
https://github.com/llvm/llvm-project/pull/204008
More information about the libcxx-commits
mailing list