[libcxx-commits] [libcxx] [libc++] Make `basic_string`'s move constructor always `noexcept` (PR #197912)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Fri May 15 04:33:43 PDT 2026
https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/197912
>From ddbb7b8c261b0eae0ec4d5c113f8c088a6a053cf Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 15 May 2026 19:20:22 +0800
Subject: [PATCH] [libc++] Make `basic_string`'s move constructor always
`noexcept`
Previously, libc++ made this move constructor unconditionally `noexcept`
since C++17. However, it was already unconditionally `noexcept` in
C++11/14 standard. See
- https://timsong-cpp.github.io/cppwp/n3337/string.cons
- https://timsong-cpp.github.io/cppwp/n4140/string.cons
So we should make it unconditionally `noexcept` even though we haven't
treated [N4258](https://wg21.link/n4258) as a DR.
---
libcxx/include/string | 13 +++-----
.../basic.string/string.cons/move.pass.cpp | 2 +-
.../string.cons/move_noexcept.pass.cpp | 13 ++++----
libcxx/test/support/test_allocator.h | 30 +++++++++++++++++++
4 files changed, 40 insertions(+), 18 deletions(-)
diff --git a/libcxx/include/string b/libcxx/include/string
index 2455938a92d9c7..56e7095233800e 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -105,8 +105,7 @@ public:
noexcept(is_nothrow_default_constructible<allocator_type>::value); // constexpr since C++20
explicit basic_string(const allocator_type& a); // constexpr since C++20
basic_string(const basic_string& str); // constexpr since C++20
- basic_string(basic_string&& str)
- noexcept(is_nothrow_move_constructible<allocator_type>::value); // constexpr since C++20
+ basic_string(basic_string&& str) noexcept; // constexpr since C++20
basic_string(const basic_string& str, size_type pos,
const allocator_type& a = allocator_type()); // constexpr since C++20
basic_string(const basic_string& str, size_type pos, size_type n,
@@ -982,13 +981,9 @@ public:
}
# ifndef _LIBCPP_CXX03_LANG
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str)
-# if _LIBCPP_STD_VER <= 14
- _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-# else
- _NOEXCEPT
-# endif
- : __rep_(std::move(__str.__rep_)), __alloc_(std::move(__str.__alloc_)) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(basic_string&& __str) _NOEXCEPT
+ : __rep_(std::move(__str.__rep_)),
+ __alloc_(std::move(__str.__alloc_)) {
__str.__rep_ = __rep();
__str.__annotate_new(0);
if (!__is_long())
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp
index 5d2d774b360638..13f2a6d1579482 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp
@@ -10,7 +10,7 @@
// <string>
-// basic_string(basic_string<charT,traits,Allocator>&& str); // constexpr since C++20
+// basic_string(basic_string&& str) noexcept; // constexpr since C++20
#include <string>
#include <cassert>
diff --git a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
index d807cac2c72c84..5e6b42272a519d 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp
@@ -10,10 +10,7 @@
// <string>
-// basic_string(basic_string&&)
-// noexcept(is_nothrow_move_constructible<allocator_type>::value); // constexpr since C++20
-
-// This tests a conforming extension
+// basic_string(basic_string&& str) noexcept; // constexpr since C++20
#include <string>
#include <cassert>
@@ -32,11 +29,11 @@ int main(int, char**) {
}
{
typedef std::basic_string<char, std::char_traits<char>, limited_allocator<char, 10>> C;
-#if TEST_STD_VER <= 14
- static_assert(!std::is_nothrow_move_constructible<C>::value, "");
-#else
static_assert(std::is_nothrow_move_constructible<C>::value, "");
-#endif
+ }
+ {
+ using C = std::basic_string<char, std::char_traits<char>, noexcept_false_ctor_allocator<char>>;
+ static_assert(std::is_nothrow_move_constructible<C>::value, "");
}
return 0;
diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h
index f8b622d7f95209..24f267d71d916d 100644
--- a/libcxx/test/support/test_allocator.h
+++ b/libcxx/test/support/test_allocator.h
@@ -515,4 +515,34 @@ struct SocccAllocator {
using propagate_on_container_swap = std::false_type;
};
+template <class T>
+struct noexcept_false_ctor_allocator {
+ using value_type = T;
+
+ TEST_CONSTEXPR noexcept_false_ctor_allocator() {}
+ template <class U>
+ TEST_CONSTEXPR noexcept_false_ctor_allocator(const noexcept_false_ctor_allocator<U&>) {}
+
+ TEST_CONSTEXPR noexcept_false_ctor_allocator(const noexcept_false_ctor_allocator&) {}
+ TEST_CONSTEXPR noexcept_false_ctor_allocator(noexcept_false_ctor_allocator&&) {}
+
+ noexcept_false_ctor_allocator& operator=(const noexcept_false_ctor_allocator&) = default;
+ noexcept_false_ctor_allocator& operator=(noexcept_false_ctor_allocator&&) = default;
+
+ TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
+
+ TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n) { return std::allocator<T>().deallocate(p, n); }
+
+ template <class U>
+ friend TEST_CONSTEXPR bool operator==(const noexcept_false_ctor_allocator, const noexcept_false_ctor_allocator<U>&) {
+ return true;
+ }
+#if TEST_STD_VER < 20
+ template <class U>
+ friend TEST_CONSTEXPR bool operator!=(const noexcept_false_ctor_allocator, const noexcept_false_ctor_allocator<U>&) {
+ return false;
+ }
+#endif
+};
+
#endif // TEST_ALLOCATOR_H
More information about the libcxx-commits
mailing list