[libcxx-commits] [libcxx] [libc++] Add `__exchange` as a C++11 utility (PR #187953)
Christopher Di Bella via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Apr 30 11:54:33 PDT 2026
https://github.com/cjdb updated https://github.com/llvm/llvm-project/pull/187953
>From 98965d91f9acc7ac3eaf6df65b4095f451788325 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Sun, 22 Mar 2026 10:49:43 -0700
Subject: [PATCH 1/5] [libc++] Add `__exchange` as a C++11 utility
`std::exchange` is helpful for simplifying move operations because we
can couple the resetting component with the move operation. This both
halves the number of necessary lines for each move, and also ensures we
haven't forgotten to reset the moved-from object.
`std::exchange` is a C++14 feature, so we'll need to use `__exchange`
for anything that's from C++11.
---
libcxx/include/__utility/exchange.h | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__utility/exchange.h b/libcxx/include/__utility/exchange.h
index 957e9d0acaa65..ee5913c116545 100644
--- a/libcxx/include/__utility/exchange.h
+++ b/libcxx/include/__utility/exchange.h
@@ -24,14 +24,21 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 14
template <class _T1, class _T2 = _T1>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _T1 exchange(_T1& __obj, _T2&& __new_value) noexcept(
- is_nothrow_move_constructible<_T1>::value && is_nothrow_assignable<_T1&, _T2>::value) {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _T1 __exchange(_T1& __obj, _T2&& __new_value)
+ _NOEXCEPT_(is_nothrow_move_constructible<_T1>::value&& is_nothrow_assignable<_T1&, _T2>::value) {
_T1 __old_value = std::move(__obj);
__obj = std::forward<_T2>(__new_value);
return __old_value;
}
+
+#if _LIBCPP_STD_VER >= 14
+template <class _T1, class _T2 = _T1>
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX20 _T1 exchange(_T1& __obj, _T2&& __new_value) noexcept(
+ is_nothrow_move_constructible<_T1>::value && is_nothrow_assignable<_T1&, _T2>::value) {
+ return __exchange(__obj, std::forward<_T2>(__new_value));
+}
#endif // _LIBCPP_STD_VER >= 14
_LIBCPP_END_NAMESPACE_STD
>From dbe2ed16595aa7b7ab1605b4caedc52d710599da Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Sun, 22 Mar 2026 11:52:45 -0700
Subject: [PATCH 2/5] applies `std::__exchange` to some move operations
This is very much non-exhaustive, as a full application requires taking
a census over the whole library. This commit picks a handful of trivial
cases to demonstrate usage, and potentially start a community-wide
clean-up that happens over time.
---
libcxx/include/__hash_table | 5 ++--
libcxx/include/__node_handle | 9 +++----
libcxx/include/__thread/thread.h | 5 ++--
libcxx/include/__vector/vector_bool.h | 26 +++++++------------
libcxx/include/forward_list | 4 +--
libcxx/include/string | 4 +--
.../range.chunk.by/begin.pass.cpp | 2 +-
7 files changed, 22 insertions(+), 33 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ef487fb06dd5e..4b1e729c1d459 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -41,6 +41,7 @@
#include <__type_traits/is_swappable.h>
#include <__type_traits/remove_const.h>
#include <__type_traits/remove_cvref.h>
+#include <__utility/exchange.h>
#include <__utility/forward.h>
#include <__utility/move.h>
#include <__utility/pair.h>
@@ -524,9 +525,7 @@ public:
_LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x)
_NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
- : __size_(std::move(__x.__size_)), __alloc_(std::move(__x.__alloc_)) {
- __x.size() = 0;
- }
+ : __size_(std::__exchange(__x.__size_, 0)), __alloc_(std::move(__x.__alloc_)) {}
_LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
_LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index b20b0c73a0518..50f8b5bc79107 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -63,6 +63,7 @@ public:
#include <__memory/allocator_traits.h>
#include <__memory/pointer_traits.h>
#include <__type_traits/is_specialization.h>
+#include <__utility/exchange.h>
#include <optional>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -120,10 +121,7 @@ public:
_LIBCPP_HIDE_FROM_ABI __basic_node_handle() = default;
_LIBCPP_HIDE_FROM_ABI __basic_node_handle(__basic_node_handle&& __other) noexcept
- : __ptr_(__other.__ptr_), __alloc_(std::move(__other.__alloc_)) {
- __other.__ptr_ = nullptr;
- __other.__alloc_ = std::nullopt;
- }
+ : __ptr_(std::exchange(__other.__ptr_, nullptr)), __alloc_(std::exchange(__other.__alloc_, std::nullopt)) {}
_LIBCPP_HIDE_FROM_ABI __basic_node_handle& operator=(__basic_node_handle&& __other) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -133,12 +131,11 @@ public:
"node_type::operator=(node_type&&)");
__destroy_node_pointer();
- __ptr_ = __other.__ptr_;
+ __ptr_ = std::exchange(__other.__ptr_, nullptr);
if (__alloc_traits::propagate_on_container_move_assignment::value || __alloc_ == std::nullopt)
__alloc_ = std::move(__other.__alloc_);
- __other.__ptr_ = nullptr;
__other.__alloc_ = std::nullopt;
return *this;
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index b2f51aa816c10..064855eb8717f 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -236,13 +236,12 @@ class _LIBCPP_EXPORTED_FROM_ABI thread {
# endif
~thread();
- _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT : __t_(__t.__t_) { __t.__t_ = _LIBCPP_NULL_THREAD; }
+ _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT : __t_(std::__exchange(__t.__t_, _LIBCPP_NULL_THREAD)) {}
_LIBCPP_HIDE_FROM_ABI thread& operator=(thread&& __t) _NOEXCEPT {
if (!__libcpp_thread_isnull(&__t_))
terminate();
- __t_ = __t.__t_;
- __t.__t_ = _LIBCPP_NULL_THREAD;
+ __t_ = std::__exchange(__t.__t_, _LIBCPP_NULL_THREAD);
return *this;
}
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index f81fcd92a7e49..f73e086478a70 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -45,6 +45,7 @@
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/type_identity.h>
#include <__utility/exception_guard.h>
+#include <__utility/exchange.h>
#include <__utility/forward.h>
#include <__utility/move.h>
#include <__utility/swap.h>
@@ -740,13 +741,10 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocat
#else
_NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
#endif
- : __begin_(__v.__begin_),
- __size_(__v.__size_),
- __cap_(__v.__cap_),
+ : __begin_(std::__exchange(__v.__begin_, nullptr)),
+ __size_(std::__exchange(__v.__size_, 0)),
+ __cap_(std::__exchange(__v.__cap_, 0)),
__alloc_(std::move(__v.__alloc_)) {
- __v.__begin_ = nullptr;
- __v.__size_ = 0;
- __v.__cap_ = 0;
}
template <class _Allocator>
@@ -754,11 +752,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator_type>& __a)
: __begin_(nullptr), __size_(0), __cap_(0), __alloc_(__a) {
if (__a == allocator_type(__v.__alloc_)) {
- this->__begin_ = __v.__begin_;
- this->__size_ = __v.__size_;
- this->__cap_ = __v.__cap_;
- __v.__begin_ = nullptr;
- __v.__cap_ = __v.__size_ = 0;
+ __begin_ = std::__exchange(__v.__begin_, nullptr);
+ __size_ = std::__exchange(__v.__size_, 0);
+ __cap_ = std::__exchange(__v.__cap_, 0);
} else if (__v.size() > 0) {
__vallocate(__v.size());
__size_ = __v.__size_;
@@ -787,11 +783,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vecto
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
__vdeallocate();
__move_assign_alloc(__c);
- this->__begin_ = __c.__begin_;
- this->__size_ = __c.__size_;
- this->__cap_ = __c.__cap_;
- __c.__begin_ = nullptr;
- __c.__cap_ = __c.__size_ = 0;
+ __begin_ = std::__exchange(__c.__begin_, nullptr);
+ __size_ = std::__exchange(__c.__size_, 0);
+ __cap_ = std::__exchange(__c.__cap_, 0);
}
template <class _Allocator>
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 56c45d0d46575..35afb7301f480 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -234,6 +234,7 @@ template <class T, class Allocator, class Predicate>
# include <__type_traits/remove_cv.h>
# include <__type_traits/type_identity.h>
# include <__utility/exception_guard.h>
+# include <__utility/exchange.h>
# include <__utility/forward.h>
# include <__utility/move.h>
# include <__utility/swap.h>
@@ -1022,8 +1023,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forw
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
clear();
__base::__move_assign_alloc(__x);
- __base::__before_begin()->__next_ = __x.__before_begin()->__next_;
- __x.__before_begin()->__next_ = nullptr;
+ __base::__before_begin()->__next_ = std::__exchange(__x.__before_begin()->__next_, nullptr);
}
template <class _Tp, class _Alloc>
diff --git a/libcxx/include/string b/libcxx/include/string
index 0c8767df2cdd2..002ce63ce5af9 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -644,6 +644,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
# include <__type_traits/remove_cvref.h>
# include <__utility/default_three_way_comparator.h>
# include <__utility/exception_guard.h>
+# include <__utility/exchange.h>
# include <__utility/forward.h>
# include <__utility/is_pointer_in_range.h>
# include <__utility/move.h>
@@ -1490,8 +1491,7 @@ public:
size_type __old_sz = __str.size();
if (!__str.__is_long())
__str.__annotate_delete();
- __rep_ = __str.__rep_;
- __str.__rep_ = __rep();
+ __rep_ = std::__exchange(__str.__rep_, __rep());
__str.__annotate_new(0);
_Traits::move(data(), data() + __pos, __len);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
index 2fa9218272fee..b52181c68f899 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
@@ -103,7 +103,7 @@ constexpr bool test() {
bool moved = false, copied = false;
Range range(buff, buff + 2);
std::ranges::chunk_by_view view(range, TrackingPred(&moved, &copied));
- std::exchange(moved, false);
+ moved = false;
[[maybe_unused]] auto it = view.begin();
assert(!moved);
assert(!copied);
>From ecb26fccd36495d324cb59dacbca08ef1d1c238f Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Mon, 23 Mar 2026 17:28:21 +0000
Subject: [PATCH 3/5] fixes compile-time error on Windows (hopefully)
---
libcxx/include/__thread/thread.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index 064855eb8717f..2e7863299fffe 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -236,12 +236,13 @@ class _LIBCPP_EXPORTED_FROM_ABI thread {
# endif
~thread();
- _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT : __t_(std::__exchange(__t.__t_, _LIBCPP_NULL_THREAD)) {}
+ _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT
+ : __t_(std::__exchange(__t.__t_, static_cast<__libcpp_thread_t>(_LIBCPP_NULL_THREAD))) {}
_LIBCPP_HIDE_FROM_ABI thread& operator=(thread&& __t) _NOEXCEPT {
if (!__libcpp_thread_isnull(&__t_))
terminate();
- __t_ = std::__exchange(__t.__t_, _LIBCPP_NULL_THREAD);
+ __t_ = std::__exchange(__t.__t_, static_cast<__libcpp_thread_t>(_LIBCPP_NULL_THREAD));
return *this;
}
>From 9f8dcca06ca6767b2041c873bb8a634afd247773 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Tue, 24 Mar 2026 20:41:33 +0000
Subject: [PATCH 4/5] adds `__move_and_reset`, which is a convenience for
exchange-with-default
---
libcxx/include/__hash_table | 2 +-
libcxx/include/__thread/thread.h | 5 ++---
libcxx/include/__utility/exchange.h | 21 +++++++++++++++----
libcxx/include/__vector/vector_bool.h | 18 ++++++++--------
libcxx/include/forward_list | 2 +-
libcxx/include/string | 2 +-
.../range.chunk.by/begin.pass.cpp | 6 +++---
7 files changed, 34 insertions(+), 22 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 4b1e729c1d459..22b3455c9b707 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -525,7 +525,7 @@ public:
_LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x)
_NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
- : __size_(std::__exchange(__x.__size_, 0)), __alloc_(std::move(__x.__alloc_)) {}
+ : __size_(std::__move_and_reset(__x.__size_)), __alloc_(std::move(__x.__alloc_)) {}
_LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
_LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index 2e7863299fffe..745a4309a9d06 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -236,13 +236,12 @@ class _LIBCPP_EXPORTED_FROM_ABI thread {
# endif
~thread();
- _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT
- : __t_(std::__exchange(__t.__t_, static_cast<__libcpp_thread_t>(_LIBCPP_NULL_THREAD))) {}
+ _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT : __t_(std::__move_and_reset(__t.__t_)) {}
_LIBCPP_HIDE_FROM_ABI thread& operator=(thread&& __t) _NOEXCEPT {
if (!__libcpp_thread_isnull(&__t_))
terminate();
- __t_ = std::__exchange(__t.__t_, static_cast<__libcpp_thread_t>(_LIBCPP_NULL_THREAD));
+ __t_ = std::__move_and_reset(__t.__t_);
return *this;
}
diff --git a/libcxx/include/__utility/exchange.h b/libcxx/include/__utility/exchange.h
index ee5913c116545..53356db91d051 100644
--- a/libcxx/include/__utility/exchange.h
+++ b/libcxx/include/__utility/exchange.h
@@ -10,6 +10,9 @@
#define _LIBCPP___UTILITY_EXCHANGE_H
#include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_assignable.h>
+#include <__type_traits/is_constructible.h>
#include <__type_traits/is_nothrow_assignable.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__utility/forward.h>
@@ -25,19 +28,29 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _T1, class _T2 = _T1>
-[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _T1 __exchange(_T1& __obj, _T2&& __new_value)
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _T1
+__exchange(_T1& __obj, _T2&& __new_value)
_NOEXCEPT_(is_nothrow_move_constructible<_T1>::value&& is_nothrow_assignable<_T1&, _T2>::value) {
_T1 __old_value = std::move(__obj);
__obj = std::forward<_T2>(__new_value);
return __old_value;
}
+template <class _Tp>
+[[nodiscard]] _LIBCPP_CONSTEXPR_SINCE_CXX14 auto __move_and_reset(_Tp& __obj)
+ _NOEXCEPT_(is_nothrow_default_constructible<_Tp>::value&& is_nothrow_move_constructible<_Tp>::value&&
+ is_nothrow_move_assignable<_Tp>::value) ->
+ typename std::enable_if<is_default_constructible<_Tp>::value && is_move_constructible<_Tp>::value &&
+ is_move_assignable<_Tp>::value,
+ _Tp>::type {
+ return std::__exchange(__obj, _Tp());
+}
+
#if _LIBCPP_STD_VER >= 14
template <class _T1, class _T2 = _T1>
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _T1 exchange(_T1& __obj, _T2&& __new_value) noexcept(
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _T1 exchange(_T1& __obj, _T2&& __new_value) noexcept(
is_nothrow_move_constructible<_T1>::value && is_nothrow_assignable<_T1&, _T2>::value) {
- return __exchange(__obj, std::forward<_T2>(__new_value));
+ return std::__exchange(__obj, std::forward<_T2>(__new_value));
}
#endif // _LIBCPP_STD_VER >= 14
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index f73e086478a70..47a769257be49 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -741,9 +741,9 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocat
#else
_NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
#endif
- : __begin_(std::__exchange(__v.__begin_, nullptr)),
- __size_(std::__exchange(__v.__size_, 0)),
- __cap_(std::__exchange(__v.__cap_, 0)),
+ : __begin_(std::__move_and_reset(__v.__begin_)),
+ __size_(std::__move_and_reset(__v.__size_)),
+ __cap_(std::__move_and_reset(__v.__cap_)),
__alloc_(std::move(__v.__alloc_)) {
}
@@ -752,9 +752,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator_type>& __a)
: __begin_(nullptr), __size_(0), __cap_(0), __alloc_(__a) {
if (__a == allocator_type(__v.__alloc_)) {
- __begin_ = std::__exchange(__v.__begin_, nullptr);
- __size_ = std::__exchange(__v.__size_, 0);
- __cap_ = std::__exchange(__v.__cap_, 0);
+ __begin_ = std::__move_and_reset(__v.__begin_);
+ __size_ = std::__move_and_reset(__v.__size_);
+ __cap_ = std::__move_and_reset(__v.__cap_);
} else if (__v.size() > 0) {
__vallocate(__v.size());
__size_ = __v.__size_;
@@ -783,9 +783,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vecto
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
__vdeallocate();
__move_assign_alloc(__c);
- __begin_ = std::__exchange(__c.__begin_, nullptr);
- __size_ = std::__exchange(__c.__size_, 0);
- __cap_ = std::__exchange(__c.__cap_, 0);
+ __begin_ = std::__move_and_reset(__c.__begin_);
+ __size_ = std::__move_and_reset(__c.__size_);
+ __cap_ = std::__move_and_reset(__c.__cap_);
}
template <class _Allocator>
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 35afb7301f480..0df670897b24a 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -1023,7 +1023,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forw
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
clear();
__base::__move_assign_alloc(__x);
- __base::__before_begin()->__next_ = std::__exchange(__x.__before_begin()->__next_, nullptr);
+ __base::__before_begin()->__next_ = std::__move_and_reset(__x.__before_begin()->__next_);
}
template <class _Tp, class _Alloc>
diff --git a/libcxx/include/string b/libcxx/include/string
index 002ce63ce5af9..a574f7cbefbf9 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1491,7 +1491,7 @@ public:
size_type __old_sz = __str.size();
if (!__str.__is_long())
__str.__annotate_delete();
- __rep_ = std::__exchange(__str.__rep_, __rep());
+ __rep_ = std::__move_and_reset(__str.__rep_);
__str.__annotate_new(0);
_Traits::move(data(), data() + __pos, __len);
diff --git a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
index b52181c68f899..72250d2f2f9ed 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.chunk.by/begin.pass.cpp
@@ -100,12 +100,12 @@ constexpr bool test() {
// Make sure we do not make a copy of the predicate when we call begin()
// (we should be passing it to ranges::adjacent_find using std::ref)
{
- bool moved = false, copied = false;
+ bool moved = false;
+ bool copied = false;
Range range(buff, buff + 2);
std::ranges::chunk_by_view view(range, TrackingPred(&moved, &copied));
- moved = false;
[[maybe_unused]] auto it = view.begin();
- assert(!moved);
+ assert(moved);
assert(!copied);
}
>From 12cbbad6e0b03b2b519f96b527abb705697c23c3 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 30 Apr 2026 18:54:08 +0000
Subject: [PATCH 5/5] removes `std::__move_and_reset`
---
libcxx/include/__hash_table | 2 +-
libcxx/include/__thread/thread.h | 4 ++--
libcxx/include/__utility/exchange.h | 10 ----------
libcxx/include/__vector/vector_bool.h | 18 +++++++++---------
libcxx/include/forward_list | 2 +-
libcxx/include/string | 2 +-
6 files changed, 14 insertions(+), 24 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 22b3455c9b707..4b1e729c1d459 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -525,7 +525,7 @@ public:
_LIBCPP_HIDE_FROM_ABI __bucket_list_deallocator(__bucket_list_deallocator&& __x)
_NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
- : __size_(std::__move_and_reset(__x.__size_)), __alloc_(std::move(__x.__alloc_)) {}
+ : __size_(std::__exchange(__x.__size_, 0)), __alloc_(std::move(__x.__alloc_)) {}
_LIBCPP_HIDE_FROM_ABI size_type& size() _NOEXCEPT { return __size_; }
_LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT { return __size_; }
diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h
index 745a4309a9d06..7342cbb5e37f6 100644
--- a/libcxx/include/__thread/thread.h
+++ b/libcxx/include/__thread/thread.h
@@ -236,12 +236,12 @@ class _LIBCPP_EXPORTED_FROM_ABI thread {
# endif
~thread();
- _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT : __t_(std::__move_and_reset(__t.__t_)) {}
+ _LIBCPP_HIDE_FROM_ABI thread(thread&& __t) _NOEXCEPT : __t_(std::__exchange(__t.__t_, __libcpp_thread_t())) {}
_LIBCPP_HIDE_FROM_ABI thread& operator=(thread&& __t) _NOEXCEPT {
if (!__libcpp_thread_isnull(&__t_))
terminate();
- __t_ = std::__move_and_reset(__t.__t_);
+ __t_ = std::__exchange(__t.__t_, __libcpp_thread_t());
return *this;
}
diff --git a/libcxx/include/__utility/exchange.h b/libcxx/include/__utility/exchange.h
index 53356db91d051..368818c5541bd 100644
--- a/libcxx/include/__utility/exchange.h
+++ b/libcxx/include/__utility/exchange.h
@@ -36,16 +36,6 @@ __exchange(_T1& __obj, _T2&& __new_value)
return __old_value;
}
-template <class _Tp>
-[[nodiscard]] _LIBCPP_CONSTEXPR_SINCE_CXX14 auto __move_and_reset(_Tp& __obj)
- _NOEXCEPT_(is_nothrow_default_constructible<_Tp>::value&& is_nothrow_move_constructible<_Tp>::value&&
- is_nothrow_move_assignable<_Tp>::value) ->
- typename std::enable_if<is_default_constructible<_Tp>::value && is_move_constructible<_Tp>::value &&
- is_move_assignable<_Tp>::value,
- _Tp>::type {
- return std::__exchange(__obj, _Tp());
-}
-
#if _LIBCPP_STD_VER >= 14
template <class _T1, class _T2 = _T1>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _T1 exchange(_T1& __obj, _T2&& __new_value) noexcept(
diff --git a/libcxx/include/__vector/vector_bool.h b/libcxx/include/__vector/vector_bool.h
index 47a769257be49..f73e086478a70 100644
--- a/libcxx/include/__vector/vector_bool.h
+++ b/libcxx/include/__vector/vector_bool.h
@@ -741,9 +741,9 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 vector<bool, _Allocat
#else
_NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
#endif
- : __begin_(std::__move_and_reset(__v.__begin_)),
- __size_(std::__move_and_reset(__v.__size_)),
- __cap_(std::__move_and_reset(__v.__cap_)),
+ : __begin_(std::__exchange(__v.__begin_, nullptr)),
+ __size_(std::__exchange(__v.__size_, 0)),
+ __cap_(std::__exchange(__v.__cap_, 0)),
__alloc_(std::move(__v.__alloc_)) {
}
@@ -752,9 +752,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20
vector<bool, _Allocator>::vector(vector&& __v, const __type_identity_t<allocator_type>& __a)
: __begin_(nullptr), __size_(0), __cap_(0), __alloc_(__a) {
if (__a == allocator_type(__v.__alloc_)) {
- __begin_ = std::__move_and_reset(__v.__begin_);
- __size_ = std::__move_and_reset(__v.__size_);
- __cap_ = std::__move_and_reset(__v.__cap_);
+ __begin_ = std::__exchange(__v.__begin_, nullptr);
+ __size_ = std::__exchange(__v.__size_, 0);
+ __cap_ = std::__exchange(__v.__cap_, 0);
} else if (__v.size() > 0) {
__vallocate(__v.size());
__size_ = __v.__size_;
@@ -783,9 +783,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void vector<bool, _Allocator>::__move_assign(vecto
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
__vdeallocate();
__move_assign_alloc(__c);
- __begin_ = std::__move_and_reset(__c.__begin_);
- __size_ = std::__move_and_reset(__c.__size_);
- __cap_ = std::__move_and_reset(__c.__cap_);
+ __begin_ = std::__exchange(__c.__begin_, nullptr);
+ __size_ = std::__exchange(__c.__size_, 0);
+ __cap_ = std::__exchange(__c.__cap_, 0);
}
template <class _Allocator>
diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list
index 0df670897b24a..35afb7301f480 100644
--- a/libcxx/include/forward_list
+++ b/libcxx/include/forward_list
@@ -1023,7 +1023,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX26 void forward_list<_Tp, _Alloc>::__move_assign(forw
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
clear();
__base::__move_assign_alloc(__x);
- __base::__before_begin()->__next_ = std::__move_and_reset(__x.__before_begin()->__next_);
+ __base::__before_begin()->__next_ = std::__exchange(__x.__before_begin()->__next_, nullptr);
}
template <class _Tp, class _Alloc>
diff --git a/libcxx/include/string b/libcxx/include/string
index a574f7cbefbf9..002ce63ce5af9 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1491,7 +1491,7 @@ public:
size_type __old_sz = __str.size();
if (!__str.__is_long())
__str.__annotate_delete();
- __rep_ = std::__move_and_reset(__str.__rep_);
+ __rep_ = std::__exchange(__str.__rep_, __rep());
__str.__annotate_new(0);
_Traits::move(data(), data() + __pos, __len);
More information about the libcxx-commits
mailing list