[libcxx-commits] [libcxx] [libc++] Simplify the implementation of string::{append, assign, assign_range} (PR #162254)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Oct 9 04:21:34 PDT 2025
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/162254
>From f064ac20c86dc72605d8e70bc58a34bfa285d2a7 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 7 Oct 2025 11:38:00 +0200
Subject: [PATCH] [libc++] Simplify the implementation of
string::{append,assign,assign_range}
---
libcxx/include/string | 40 ++++++-------------
.../string_append/iterator.pass.cpp | 15 +++++--
libcxx/test/support/test_iterators.h | 38 ++++++++++++++++++
3 files changed, 62 insertions(+), 31 deletions(-)
diff --git a/libcxx/include/string b/libcxx/include/string
index 363f27a51648c..1db64e36ae972 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -698,6 +698,8 @@ __concatenate_strings(const _Allocator& __alloc,
__type_identity_t<basic_string_view<_CharT, _Traits> > __str1,
__type_identity_t<basic_string_view<_CharT, _Traits> > __str2);
+// This is true if we know for a fact that dereferencing the iterator won't access any part of the `string` we're
+// modifying if __addr_in_range(*it) returns false.
template <class _Iter>
inline const bool __string_is_trivial_iterator_v = false;
@@ -1413,24 +1415,16 @@ public:
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
- template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
- append(_InputIterator __first, _InputIterator __last) {
- const basic_string __temp(__first, __last, __alloc_);
- append(__temp.data(), __temp.size());
- return *this;
- }
-
- template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
+ template <class _InputIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
- append(_ForwardIterator __first, _ForwardIterator __last) {
- size_type __sz = size();
- size_type __cap = capacity();
- size_type __n = static_cast<size_type>(std::distance(__first, __last));
- if (__n == 0)
- return *this;
+ append(_InputIterator __first, _InputIterator __last) {
+ if (__string_is_trivial_iterator_v<_InputIterator> && !__addr_in_range(*__first)) {
+ size_type __sz = size();
+ size_type __cap = capacity();
+ size_type __n = static_cast<size_type>(std::distance(__first, __last));
+ if (__n == 0)
+ return *this;
- if (__string_is_trivial_iterator_v<_ForwardIterator> && !__addr_in_range(*__first)) {
if (__cap - __sz < __n)
__grow_by_without_replace(__cap, __sz + __n - __cap, __sz, __sz, 0);
__annotate_increase(__n);
@@ -1540,17 +1534,10 @@ public:
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(size_type __n, value_type __c);
- template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
+ template <class _InputIterator>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
assign(_InputIterator __first, _InputIterator __last) {
- __assign_with_sentinel(__first, __last);
- return *this;
- }
-
- template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> = 0>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
- assign(_ForwardIterator __first, _ForwardIterator __last) {
- if (__string_is_trivial_iterator_v<_ForwardIterator>) {
+ if _LIBCPP_CONSTEXPR (__string_is_trivial_iterator_v<_InputIterator>) {
size_type __n = static_cast<size_type>(std::distance(__first, __last));
__assign_trivial(__first, __last, __n);
} else {
@@ -1563,8 +1550,7 @@ public:
# if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_CharT> _Range>
_LIBCPP_HIDE_FROM_ABI constexpr basic_string& assign_range(_Range&& __range) {
- if constexpr (__string_is_trivial_iterator_v<ranges::iterator_t<_Range>> &&
- (ranges::forward_range<_Range> || ranges::sized_range<_Range>)) {
+ if constexpr (__string_is_trivial_iterator_v<ranges::iterator_t<_Range>>) {
size_type __n = static_cast<size_type>(ranges::distance(__range));
__assign_trivial(ranges::begin(__range), ranges::end(__range), __n);
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
index 684661aeab8fc..e357f65408bae 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/iterator.pass.cpp
@@ -11,13 +11,14 @@
// template<class InputIterator>
// basic_string& append(InputIterator first, InputIterator last); // constexpr since C++20
-#include <string>
#include <cassert>
+#include <string>
+#include <vector>
-#include "test_macros.h"
-#include "test_iterators.h"
-#include "min_allocator.h"
#include "asan_testing.h"
+#include "min_allocator.h"
+#include "test_iterators.h"
+#include "test_macros.h"
template <class S, class It>
TEST_CONSTEXPR_CXX20 void test(S s, It first, It last, S expected) {
@@ -221,6 +222,12 @@ TEST_CONSTEXPR_CXX20 void test_string() {
s.append(MoveIt(It(std::begin(p))), MoveIt(It(std::end(p) - 1)));
assert(s == "ABCD");
}
+ {
+ std::vector<char> buffer(5, 'a');
+ S s;
+ s.append(single_pass_iterator<std::vector<char>>(buffer), single_pass_iterator<std::vector<char> >());
+ assert(s == "aaaaa");
+ }
}
TEST_CONSTEXPR_CXX20 bool test() {
diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h
index 0335a4c561017..6a2f2bae7f297 100644
--- a/libcxx/test/support/test_iterators.h
+++ b/libcxx/test/support/test_iterators.h
@@ -115,6 +115,44 @@ template <class It>
cpp17_input_iterator(It) -> cpp17_input_iterator<It>;
#endif
+template <class Container>
+class single_pass_iterator {
+ Container* container_;
+
+public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = typename Container::value_type;
+ using difference_type = typename Container::difference_type;
+ using pointer = typename Container::pointer;
+ using reference = typename Container::reference;
+
+ TEST_CONSTEXPR explicit single_pass_iterator() = default;
+ TEST_CONSTEXPR explicit single_pass_iterator(Container& container)
+ : container_(container.empty() ? nullptr : &container) {}
+
+ TEST_CONSTEXPR reference operator*() const { return container_->back(); }
+
+ TEST_CONSTEXPR_CXX14 single_pass_iterator& operator++() {
+ container_->pop_back();
+ if (container_->empty())
+ container_ = nullptr;
+ return *this;
+ }
+
+ TEST_CONSTEXPR_CXX14 single_pass_iterator operator++(int) { return ++(*this); }
+
+ friend TEST_CONSTEXPR bool operator==(const single_pass_iterator& lhs, const single_pass_iterator& rhs) {
+ return lhs.container_ == rhs.container_;
+ }
+
+ friend TEST_CONSTEXPR bool operator!=(const single_pass_iterator& lhs, const single_pass_iterator& rhs) {
+ return !(lhs == rhs);
+ }
+
+ template <class T>
+ void operator,(const T&) = delete;
+};
+
#if TEST_STD_VER > 17
static_assert(std::input_iterator<cpp17_input_iterator<int*>>);
#endif
More information about the libcxx-commits
mailing list