[libcxx-commits] [libcxx] [libc++] Avoid passing both sentinel and size around in vector (PR #214132)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Aug 10 03:02:14 PDT 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/214132
>From 9bafb826944eb3de2df9820821a4858b3218c80a Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 5 Aug 2026 06:35:31 +0200
Subject: [PATCH] [libc++] Avoid passing both sentinel and size around in
vector
---
.../__memory/uninitialized_algorithms.h | 30 ++++----
libcxx/include/__vector/vector.h | 73 +++++++++----------
libcxx/include/condition_variable | 4 +
.../uninitialized_allocator_copy.pass.cpp | 4 +-
.../test/libcxx/transitive_includes/cxx26.csv | 1 -
5 files changed, 56 insertions(+), 56 deletions(-)
diff --git a/libcxx/include/__memory/uninitialized_algorithms.h b/libcxx/include/__memory/uninitialized_algorithms.h
index e1c6fe25040ea..01c947fd5de4e 100644
--- a/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/libcxx/include/__memory/uninitialized_algorithms.h
@@ -10,10 +10,8 @@
#ifndef _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
#define _LIBCPP___MEMORY_UNINITIALIZED_ALGORITHMS_H
-#include <__algorithm/copy.h>
#include <__algorithm/in_out_result.h>
#include <__algorithm/unwrap_iter.h>
-#include <__algorithm/unwrap_range.h>
#include <__config>
#include <__fwd/memory.h>
#include <__iterator/iterator_traits.h>
@@ -23,7 +21,9 @@
#include <__memory/construct_at.h>
#include <__memory/destroy.h>
#include <__memory/pointer_traits.h>
+#include <__string/constexpr_c_functions.h>
#include <__type_traits/enable_if.h>
+#include <__type_traits/is_always_bitcastable.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_reference.h>
#include <__type_traits/is_same.h>
@@ -323,13 +323,13 @@ class _AllocatorDestroyRangeReverse {
//
// The caller has to ensure that __first2 can hold at least N uninitialized elements. If an exception is thrown the
// already copied elements are destroyed in reverse order of their construction.
-template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
+template <class _Alloc, class _Iter1, class _Size, class _Iter2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
-__uninitialized_allocator_copy_impl(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
+__uninitialized_allocator_copy_n_impl(_Alloc& __alloc, _Iter1 __first1, _Size __count, _Iter2 __first2) {
auto __destruct_first = __first2;
auto __guard =
std::__make_exception_guard(_AllocatorDestroyRangeReverse<_Alloc, _Iter2>(__alloc, __destruct_first, __first2));
- while (__first1 != __last1) {
+ for (_Size __i = 0; __i != __count; ++__i) {
allocator_traits<_Alloc>::construct(__alloc, std::__to_address(__first2), *__first1);
++__first1;
++__first2;
@@ -346,31 +346,33 @@ inline const bool __allocator_has_trivial_copy_construct_v<allocator<_Type>, _Ty
template <class _Alloc,
class _In,
+ class _Size,
class _Out,
- __enable_if_t<is_trivially_copy_constructible<_In>::value && is_trivially_assignable<_Out&, _In&>::value &&
+ __enable_if_t<__is_always_bitcastable<_In, _Out>::value && is_trivially_constructible<_Out, _In>::value &&
+ is_trivially_assignable<_Out&, _In&>::value &&
is_same<__remove_const_t<_In>, __remove_const_t<_Out> >::value &&
__allocator_has_trivial_copy_construct_v<_Alloc, _In>,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Out*
-__uninitialized_allocator_copy_impl(_Alloc&, _In* __first1, _In* __last1, _Out* __first2) {
+__uninitialized_allocator_copy_n_impl(_Alloc&, _In* __first1, _Size __count, _Out* __first2) {
if (__libcpp_is_constant_evaluated()) {
- while (__first1 != __last1) {
+ for (_Size __i = 0; __i != __count; ++__i) {
std::__construct_at(std::__to_address(__first2), *__first1);
++__first1;
++__first2;
}
return __first2;
} else {
- return std::copy(__first1, __last1, __first2);
+ std::__constexpr_memmove(__first2, __first1, __element_count(__count));
+ return __first2 + __count;
}
}
-template <class _Alloc, class _Iter1, class _Sent1, class _Iter2>
+template <class _Alloc, class _Iter1, class _Size, class _Iter2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter2
-__uninitialized_allocator_copy(_Alloc& __alloc, _Iter1 __first1, _Sent1 __last1, _Iter2 __first2) {
- auto __unwrapped_range = std::__unwrap_range(std::move(__first1), std::move(__last1));
- auto __result = std::__uninitialized_allocator_copy_impl(
- __alloc, std::move(__unwrapped_range.first), std::move(__unwrapped_range.second), std::__unwrap_iter(__first2));
+__uninitialized_allocator_copy_n(_Alloc& __alloc, _Iter1 __first1, _Size __count, _Iter2 __first2) {
+ auto __result = std::__uninitialized_allocator_copy_n_impl(
+ __alloc, std::__unwrap_iter(std::move(__first1)), __count, std::__unwrap_iter(__first2));
return std::__rewrap_iter(__first2, __result);
}
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 5d2ef8fc2a583..139092eab260d 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -209,7 +209,7 @@ class _LIBCPP_WARN_UNUSED vector {
int> = 0>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(_ForwardIterator __first, _ForwardIterator __last) {
size_type __n = static_cast<size_type>(std::distance(__first, __last));
- __init_with_size(__first, __last, __n);
+ __init_with_size(__first, __n);
}
template <
@@ -221,7 +221,7 @@ class _LIBCPP_WARN_UNUSED vector {
vector(_ForwardIterator __first, _ForwardIterator __last, const allocator_type& __a)
: __layout_(__a) {
size_type __n = static_cast<size_type>(std::distance(__first, __last));
- __init_with_size(__first, __last, __n);
+ __init_with_size(__first, __n);
}
#if _LIBCPP_STD_VER >= 23
@@ -230,7 +230,7 @@ class _LIBCPP_WARN_UNUSED vector {
: __layout_(__a) {
if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
auto __n = static_cast<size_type>(ranges::distance(__range));
- __init_with_size(ranges::begin(__range), ranges::end(__range), __n);
+ __init_with_size(ranges::begin(__range), __n);
} else {
__init_with_sentinel(ranges::begin(__range), ranges::end(__range));
@@ -262,24 +262,24 @@ class _LIBCPP_WARN_UNUSED vector {
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(const vector& __x)
: __layout_(__alloc_traits::select_on_container_copy_construction(__x.__layout_.__alloc())) {
- __init_with_size(__x.__layout_.__begin_ptr(), __x.__layout_.__end_ptr(), __x.size());
+ __init_with_size(__x.__layout_.__begin_ptr(), __x.size());
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
vector(const vector& __x, const __type_identity_t<allocator_type>& __a)
: __layout_(__a) {
- __init_with_size(__x.__layout_.__begin_ptr(), __x.__layout_.__end_ptr(), __x.size());
+ __init_with_size(__x.__layout_.__begin_ptr(), __x.size());
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector& operator=(const vector& __x);
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector(initializer_list<value_type> __il) {
- __init_with_size(__il.begin(), __il.end(), __il.size());
+ __init_with_size(__il.begin(), __il.size());
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
vector(initializer_list<value_type> __il, const allocator_type& __a)
: __layout_(__a) {
- __init_with_size(__il.begin(), __il.end(), __il.size());
+ __init_with_size(__il.begin(), __il.size());
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI vector& operator=(initializer_list<value_type> __il) {
@@ -316,7 +316,7 @@ class _LIBCPP_WARN_UNUSED vector {
is_constructible<value_type, typename iterator_traits<_ForwardIterator>::reference>::value,
int> = 0>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void assign(_ForwardIterator __first, _ForwardIterator __last) {
- __assign_with_size<_ClassicAlgPolicy>(__first, __last, std::distance(__first, __last));
+ __assign_with_size<_ClassicAlgPolicy>(__first, std::distance(__first, __last));
}
#if _LIBCPP_STD_VER >= 23
@@ -324,7 +324,7 @@ class _LIBCPP_WARN_UNUSED vector {
_LIBCPP_HIDE_FROM_ABI constexpr void assign_range(_Range&& __range) {
if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
auto __n = static_cast<size_type>(ranges::distance(__range));
- __assign_with_size<_RangeAlgPolicy>(ranges::begin(__range), ranges::end(__range), __n);
+ __assign_with_size<_RangeAlgPolicy>(ranges::begin(__range), __n);
} else {
__assign_with_sentinel(ranges::begin(__range), ranges::end(__range));
@@ -484,7 +484,7 @@ class _LIBCPP_WARN_UNUSED vector {
if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
auto __len = ranges::distance(__range);
if (__len <= static_cast<difference_type>(__layout_.__remaining_capacity())) {
- __construct_at_end(ranges::begin(__range), ranges::end(__range), __len);
+ __construct_at_end(ranges::begin(__range), __len);
} else {
_SplitBuffer __buffer(__recommend(size() + __len), size(), __layout_.__alloc());
__buffer.__construct_at_end_with_size(ranges::begin(__range), __len);
@@ -529,7 +529,7 @@ class _LIBCPP_WARN_UNUSED vector {
int> = 0>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
insert(const_iterator __position, _ForwardIterator __first, _ForwardIterator __last) {
- return __insert_with_size<_ClassicAlgPolicy>(__position, __first, __last, std::distance(__first, __last));
+ return __insert_with_size<_ClassicAlgPolicy>(__position, __first, std::distance(__first, __last));
}
#if _LIBCPP_STD_VER >= 23
@@ -537,7 +537,7 @@ class _LIBCPP_WARN_UNUSED vector {
_LIBCPP_HIDE_FROM_ABI constexpr iterator insert_range(const_iterator __position, _Range&& __range) {
if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
auto __n = static_cast<size_type>(ranges::distance(__range));
- return __insert_with_size<_RangeAlgPolicy>(__position, ranges::begin(__range), ranges::end(__range), __n);
+ return __insert_with_size<_RangeAlgPolicy>(__position, ranges::begin(__range), __n);
} else {
return __insert_with_sentinel(__position, ranges::begin(__range), ranges::end(__range));
@@ -614,14 +614,13 @@ class _LIBCPP_WARN_UNUSED vector {
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __construct_at_end(size_type __n);
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __construct_at_end(size_type __n, const_reference __x);
- template <class _InputIterator, class _Sentinel>
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
- __init_with_size(_InputIterator __first, _Sentinel __last, size_type __n) {
+ template <class _InputIterator>
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __init_with_size(_InputIterator __first, size_type __n) {
auto __guard = std::__make_exception_guard(__destroy_vector(*this));
if (__n > 0) {
__vallocate(__n);
- __construct_at_end(std::move(__first), std::move(__last), __n);
+ __construct_at_end(std::move(__first), __n);
}
__guard.__complete();
@@ -644,9 +643,8 @@ class _LIBCPP_WARN_UNUSED vector {
// The `_Iterator` in `*_with_size` functions can be input-only only if called from `*_range` (since C++23).
// Otherwise, `_Iterator` is a forward iterator.
- template <class _AlgPolicy, class _Iterator, class _Sentinel>
- _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
- __assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n);
+ template <class _AlgPolicy, class _Iterator>
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __assign_with_size(_Iterator __first, difference_type __n);
template <class _AlgPolicy,
class _Iterator,
@@ -671,13 +669,13 @@ class _LIBCPP_WARN_UNUSED vector {
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
__insert_with_sentinel(const_iterator __position, _InputIterator __first, _Sentinel __last);
- template <class _AlgPolicy, class _Iterator, class _Sentinel>
+ template <class _AlgPolicy, class _Iterator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
- __insert_with_size(const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n);
+ __insert_with_size(const_iterator __position, _Iterator __first, difference_type __n);
- template <class _InputIterator, class _Sentinel>
+ template <class _InputIterator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
- __construct_at_end(_InputIterator __first, _Sentinel __last, size_type __n);
+ __construct_at_end(_InputIterator __first, size_type __n);
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator __make_iter(pointer __p) _NOEXCEPT {
#ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_VECTOR
@@ -901,12 +899,11 @@ vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x)
}
template <class _Tp, class _Allocator>
-template <class _InputIterator, class _Sentinel>
+template <class _InputIterator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 void
-vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, _Sentinel __last, size_type __n) {
+vector<_Tp, _Allocator>::__construct_at_end(_InputIterator __first, size_type __n) {
_ConstructTransaction __tx(*this, __n);
- __tx.__pos_ = std::__uninitialized_allocator_copy(
- this->__layout_.__alloc(), std::move(__first), std::move(__last), __tx.__pos_);
+ __tx.__pos_ = std::__uninitialized_allocator_copy_n(this->__layout_.__alloc(), std::move(__first), __n, __tx.__pos_);
}
template <class _Tp, class _Allocator>
@@ -926,8 +923,7 @@ vector<_Tp, _Allocator>::vector(vector&& __x, const __type_identity_t<allocator_
if (__a == __x.__layout_.__alloc()) {
__layout_.__move_assign_without_allocator(__x.__layout_);
} else {
- typedef move_iterator<iterator> _Ip;
- __init_with_size(_Ip(__x.begin()), _Ip(__x.end()), __x.size());
+ __init_with_size(move_iterator<iterator>(__x.begin()), __x.size());
}
}
@@ -976,23 +972,23 @@ vector<_Tp, _Allocator>::__assign_with_sentinel(_Iterator __first, _Sentinel __l
}
template <class _Tp, class _Allocator>
-template <class _AlgPolicy, class _Iterator, class _Sentinel>
+template <class _AlgPolicy, class _Iterator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
-vector<_Tp, _Allocator>::__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n) {
+vector<_Tp, _Allocator>::__assign_with_size(_Iterator __first, difference_type __n) {
size_type __new_size = static_cast<size_type>(__n);
if (__new_size <= capacity()) {
auto const __size = size();
if (__new_size > __size) {
auto __mid = std::__copy_n<_AlgPolicy>(std::move(__first), __size, this->__layout_.__begin_ptr()).__in_;
- __construct_at_end(std::move(__mid), std::move(__last), __new_size - __size);
+ __construct_at_end(std::move(__mid), __new_size - __size);
} else {
- pointer __m = std::__copy(std::move(__first), __last, this->__layout_.__begin_ptr()).__out_;
+ pointer __m = std::__copy_n<_AlgPolicy>(std::move(__first), __n, this->__layout_.__begin_ptr()).__out_;
this->__destruct_at_end(__m);
}
} else {
__vdeallocate();
__vallocate(__recommend(__new_size));
- __construct_at_end(std::move(__first), std::move(__last), __new_size);
+ __construct_at_end(std::move(__first), __new_size);
}
}
@@ -1265,10 +1261,9 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
}
template <class _Tp, class _Allocator>
-template <class _AlgPolicy, class _Iterator, class _Sentinel>
+template <class _AlgPolicy, class _Iterator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
-vector<_Tp, _Allocator>::__insert_with_size(
- const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) {
+vector<_Tp, _Allocator>::__insert_with_size(const_iterator __position, _Iterator __first, difference_type __n) {
pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
if (__n > 0) {
if (__n <= static_cast<difference_type>(__layout_.__remaining_capacity())) {
@@ -1278,13 +1273,13 @@ vector<_Tp, _Allocator>::__insert_with_size(
if (__n > __dx) {
#if _LIBCPP_STD_VER >= 23
if constexpr (!forward_iterator<_Iterator>) {
- __construct_at_end(std::move(__first), std::move(__last), __n);
+ __construct_at_end(std::move(__first), __n);
std::rotate(__p, __old_last, __end);
} else
#endif
{
_Iterator __m = std::next(__first, __dx);
- __construct_at_end(__m, __last, __n - __dx);
+ __construct_at_end(__m, __n - __dx);
if (__dx > 0) {
__move_range(__p, __old_last, __p + __n);
__insert_assign_n_unchecked<_AlgPolicy>(__first, __dx, __p);
diff --git a/libcxx/include/condition_variable b/libcxx/include/condition_variable
index cefb3ffff879f..2bb4c1b0a8ad9 100644
--- a/libcxx/include/condition_variable
+++ b/libcxx/include/condition_variable
@@ -350,6 +350,10 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
+# if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
+# include <initializer_list>
+# endif
+
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
#endif // _LIBCPP_CONDITION_VARIABLE
diff --git a/libcxx/test/libcxx/memory/uninitialized_allocator_copy.pass.cpp b/libcxx/test/libcxx/memory/uninitialized_allocator_copy.pass.cpp
index 1d127f947c1da..cacbd980df835 100644
--- a/libcxx/test/libcxx/memory/uninitialized_allocator_copy.pass.cpp
+++ b/libcxx/test/libcxx/memory/uninitialized_allocator_copy.pass.cpp
@@ -58,8 +58,8 @@ int main(int, char**) {
ThrowSometimes in[20];
TEST_ALIGNAS_TYPE(ThrowSometimes) char out[sizeof(ThrowSometimes) * 20];
try {
- std::__uninitialized_allocator_copy(
- alloc, std::begin(in), std::end(in), reinterpret_cast<ThrowSometimes*>(std::begin(out)));
+ std::__uninitialized_allocator_copy_n(
+ alloc, std::begin(in), std::size(in), reinterpret_cast<ThrowSometimes*>(std::begin(out)));
} catch (...) {
}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index caf94dc4b2ca5..e575c2b063d54 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -187,7 +187,6 @@ condition_variable climits
condition_variable cstdint
condition_variable cstring
condition_variable ctime
-condition_variable initializer_list
condition_variable limits
condition_variable ratio
condition_variable typeinfo
More information about the libcxx-commits
mailing list