[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