[libcxx-commits] [libcxx] [libc++] Make `basic_string`'s move constructor always `noexcept` (PR #197912)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri May 15 04:23:27 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: A. Jiang (frederick-vs-ja)

<details>
<summary>Changes</summary>

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.

---
Full diff: https://github.com/llvm/llvm-project/pull/197912.diff


4 Files Affected:

- (modified) libcxx/include/string (+4-9) 
- (modified) libcxx/test/std/strings/basic.string/string.cons/move.pass.cpp (+1-1) 
- (modified) libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp (+5-8) 
- (modified) libcxx/test/support/test_allocator.h (+30) 


``````````diff
diff --git a/libcxx/include/string b/libcxx/include/string
index 2455938a92d9c..56e7095233800 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 5d2d774b36063..13f2a6d157948 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 d807cac2c72c8..5e6b42272a519 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 f8b622d7f9520..285de8824eb58 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

``````````

</details>


https://github.com/llvm/llvm-project/pull/197912


More information about the libcxx-commits mailing list