[libcxx-commits] [libcxx] WIP [libc++][string] P3044R2: sub-`string_view` from `string` (PR #147095)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 4 11:36:20 PDT 2025


https://github.com/H-G-Hristov created https://github.com/llvm/llvm-project/pull/147095

Implements [P3044R2](https://wg21.link/P3044R2)

- https://github.com/cplusplus/draft/pull/7975

>From 554a97e3c7f57202a98b6b7c23e928f690efcb91 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 4 Jul 2025 18:22:03 +0300
Subject: [PATCH 1/2] WIP [libc++][string] P3044R2: sub-`string_view` from
 `string`

Implements [P3044R2](https://wg21.link/P3044R2)

-  https://github.com/cplusplus/draft/pull/7975
---
 libcxx/include/string                         |   9 +
 libcxx/include/string_view                    |   8 +
 .../string.ops/string_substr/subview.pass.cpp |  33 ++++
 .../string.view.ops/substr.pass.cpp           | 125 --------------
 .../string.view.ops/substr_subview.pass.cpp   | 158 ++++++++++++++++++
 5 files changed, 208 insertions(+), 125 deletions(-)
 create mode 100644 libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp
 delete mode 100644 libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
 create mode 100644 libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp

diff --git a/libcxx/include/string b/libcxx/include/string
index c450ddcb41914..9476c3df3b8e7 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -280,6 +280,8 @@ public:
     basic_string substr(size_type pos = 0, size_type n = npos) const;                           // constexpr in C++20, removed in C++23
     basic_string substr(size_type pos = 0, size_type n = npos) const&;                          // since C++23
     constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&;                    // since C++23
+    constexpr basic_string_view<charT, traits> subview(size_type pos = 0,
+                                                       size_type n = npos) const;               // since C++26
     void swap(basic_string& str)
         noexcept(allocator_traits<allocator_type>::propagate_on_container_swap::value ||
                  allocator_traits<allocator_type>::is_always_equal::value);                     // C++17, constexpr since C++20
@@ -1745,6 +1747,13 @@ public:
   }
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view<_CharT, _Traits>
+  subview(size_type __pos = 0, size_type __n = npos) const {
+    return basic_string_view<_CharT, _Traits>(*this).subview(__pos, __n);
+  }
+#  endif
+
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(basic_string& __str)
 #  if _LIBCPP_STD_VER >= 14
       _NOEXCEPT;
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 861187c0640e1..ad036882e1630 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -130,6 +130,8 @@ namespace std {
       size_type copy(charT* s, size_type n, size_type pos = 0) const;  // constexpr in C++20
 
       constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
+      constexpr basic_string_view subview(size_type pos = 0,
+                                          size_type n = npos) const;      // freestanding-deleted, since C++26
       constexpr int compare(basic_string_view s) const noexcept;
       constexpr int compare(size_type pos1, size_type n1, basic_string_view s) const;
       constexpr int compare(size_type pos1, size_type n1,
@@ -465,6 +467,12 @@ public:
                           : basic_string_view(__assume_valid(), data() + __pos, std::min(__n, size() - __pos));
   }
 
+#  if _LIBCPP_STD_VER >= 26
+  _LIBCPP_HIDE_FROM_ABI constexpr basic_string_view subview(size_type __pos = 0, size_type __n = npos) const {
+    return this->substr(__pos, __n);
+  }
+#  endif
+
   _LIBCPP_CONSTEXPR_SINCE_CXX14 int compare(basic_string_view __sv) const _NOEXCEPT {
     size_type __rlen = std::min(size(), __sv.size());
     int __retval     = _Traits::compare(data(), __sv.data(), __rlen);
diff --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp
new file mode 100644
index 0000000000000..65418dcb4d7be
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/subview.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <string>
+
+// constexpr basic_string_view<_CharT, _Traits> subview(size_type __pos = 0, size_type __n = npos) const;
+
+#include <cassert>
+#include <string>
+
+constexpr bool test() {
+  std::string s{"Hello cruel world!"};
+  auto sub = s.subview(6);
+  assert(sub == "cruel world!");
+  auto subsub = sub.subview(0, 5);
+  assert(subsub == "cruel");
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
deleted file mode 100644
index 62b0259c175f8..0000000000000
--- a/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: !stdlib=libc++ && (c++03 || c++11 || c++14)
-
-// <string_view>
-
-// constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
-
-// Throws: out_of_range if pos > size().
-// Effects: Determines the effective length rlen of the string to reference as the smaller of n and size() - pos.
-// Returns: basic_string_view(data()+pos, rlen).
-
-#include <algorithm>
-#include <cassert>
-#include <cstddef>
-#include <stdexcept>
-#include <string_view>
-
-#include "test_macros.h"
-
-template <typename CharT>
-void test1(std::basic_string_view<CharT> sv, std::size_t n, size_t pos) {
-  std::basic_string_view<CharT> sv1;
-#ifdef TEST_HAS_NO_EXCEPTIONS
-  if (pos > sv.size())
-    return; // would throw if exceptions were enabled
-  sv1 = sv.substr(pos, n);
-#else
-  try {
-    sv1 = sv.substr(pos, n);
-    assert(pos <= sv.size());
-  } catch (const std::out_of_range&) {
-    assert(pos > sv.size());
-    return;
-  }
-#endif
-  const std::size_t rlen = std::min(n, sv.size() - pos);
-  assert(sv1.size() == rlen);
-  for (std::size_t i = 0; i < rlen; ++i)
-    assert(sv[pos + i] == sv1[i]);
-}
-
-template <typename CharT>
-void test(const CharT* s) {
-  typedef std::basic_string_view<CharT> string_view_t;
-
-  string_view_t sv1(s);
-
-  test1(sv1, 0, 0);
-  test1(sv1, 1, 0);
-  test1(sv1, 20, 0);
-  test1(sv1, sv1.size(), 0);
-
-  test1(sv1, 0, 3);
-  test1(sv1, 2, 3);
-  test1(sv1, 100, 3);
-
-  test1(sv1, 0, string_view_t::npos);
-  test1(sv1, 2, string_view_t::npos);
-  test1(sv1, sv1.size(), string_view_t::npos);
-
-  test1(sv1, sv1.size() + 1, 0);
-  test1(sv1, sv1.size() + 1, 1);
-  test1(sv1, sv1.size() + 1, string_view_t::npos);
-}
-
-int main(int, char**) {
-  test("ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test("ABCDE");
-  test("a");
-  test("");
-
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test(L"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test(L"ABCDE");
-  test(L"a");
-  test(L"");
-#endif
-
-#if TEST_STD_VER >= 11
-  test(u"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test(u"ABCDE");
-  test(u"a");
-  test(u"");
-
-  test(U"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
-  test(U"ABCDE");
-  test(U"a");
-  test(U"");
-#endif
-
-#if TEST_STD_VER > 11
-  {
-    constexpr std::string_view sv1{"ABCDE", 5};
-
-    {
-      constexpr std::string_view sv2 = sv1.substr(0, 3);
-      static_assert(sv2.size() == 3, "");
-      static_assert(sv2[0] == 'A', "");
-      static_assert(sv2[1] == 'B', "");
-      static_assert(sv2[2] == 'C', "");
-    }
-
-    {
-      constexpr std::string_view sv2 = sv1.substr(3, 0);
-      static_assert(sv2.size() == 0, "");
-    }
-
-    {
-      constexpr std::string_view sv2 = sv1.substr(3, 3);
-      static_assert(sv2.size() == 2, "");
-      static_assert(sv2[0] == 'D', "");
-      static_assert(sv2[1] == 'E', "");
-    }
-  }
-#endif
-
-  return 0;
-}
diff --git a/libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp
new file mode 100644
index 0000000000000..c40c771db4bae
--- /dev/null
+++ b/libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: !stdlib=libc++ && (c++03 || c++11 || c++14)
+
+// <string_view>
+
+// constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const;
+// constexpr basic_string_view subview(size_type pos = 0,
+//                                     size_type n = npos) const;      // freestanding-deleted
+
+// subview is alternative name of substr
+
+// Throws: out_of_range if pos > size().
+// Effects: Determines the effective length rlen of the string to reference as the smaller of n and size() - pos.
+// Returns: basic_string_view(data()+pos, rlen).
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <stdexcept>
+#include <string_view>
+
+#include "test_macros.h"
+
+template <typename CharT>
+struct Test {
+  typedef std::basic_string_view<CharT> (std::basic_string_view<CharT>::*Sub)(
+      typename std::basic_string_view<CharT>::size_type, typename std::basic_string_view<CharT>::size_type) const;
+};
+
+template <typename CharT>
+void testDetail(std::basic_string_view<CharT> sv, typename Test<CharT>::Sub testSub, std::size_t n, size_t pos) {
+  (void)testSub; // Avoid unused parameter warning
+  std::basic_string_view<CharT> sv1;
+#ifdef TEST_HAS_NO_EXCEPTIONS
+  if (pos > sv.size())
+    return; // would throw if exceptions were enabled
+  sv1 = (sv.*testSub)(pos, n);
+#else
+  try {
+    sv1 = (sv.*testSub)(pos, n);
+    assert(pos <= sv.size());
+  } catch (const std::out_of_range&) {
+    assert(pos > sv.size());
+    return;
+  }
+#endif
+  const std::size_t rlen = std::min(n, sv.size() - pos);
+  assert(sv1.size() == rlen);
+  for (std::size_t i = 0; i < rlen; ++i)
+    assert(sv[pos + i] == sv1[i]);
+}
+
+template <typename CharT>
+void testCases(std::basic_string_view<CharT> sv, typename Test<CharT>::Sub testSub) {
+  testDetail(sv, testSub, 0, 0);
+  testDetail(sv, testSub, 1, 0);
+  testDetail(sv, testSub, 20, 0);
+  testDetail(sv, testSub, sv.size(), 0);
+
+  testDetail(sv, testSub, 100, 3);
+
+  testDetail(sv, testSub, 0, std::basic_string_view<CharT>::npos);
+  testDetail(sv, testSub, 2, std::basic_string_view<CharT>::npos);
+  testDetail(sv, testSub, sv.size(), std::basic_string_view<CharT>::npos);
+
+  testDetail(sv, testSub, sv.size() + 1, 0);
+  testDetail(sv, testSub, sv.size() + 1, 1);
+  testDetail(sv, testSub, sv.size() + 1, std::basic_string_view<CharT>::npos);
+}
+
+template <typename CharT>
+void testSubs(const CharT* s) {
+  std::basic_string_view<CharT> sv(s);
+
+  testCases(sv, &std::basic_string_view<CharT>::substr);
+#if TEST_STD_VER >= 26
+  testCases(sv, &std::basic_string_view<CharT>::subview);
+#endif // TEST_STD_VER >= 26
+}
+
+void test() {
+  testSubs("ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs("ABCDE");
+  testSubs("a");
+  testSubs("");
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  testSubs(
+      L"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs(L"ABCDE");
+  testSubs(L"a");
+  testSubs(L"");
+#endif
+
+#if TEST_STD_VER >= 11
+  testSubs(
+      u"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs(u"ABCDE");
+  testSubs(u"a");
+  testSubs(u"");
+
+  testSubs(
+      U"ABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDEABCDE");
+  testSubs(U"ABCDE");
+  testSubs(U"a");
+  testSubs(U"");
+#endif
+}
+
+#if TEST_STD_VER >= 14
+template <typename Test<char>::Sub TestSub>
+constexpr void testConstexprDetail() {
+  constexpr std::string_view sv{"ABCDE", 5};
+  {
+    constexpr std::string_view sv2 = (sv.*TestSub)(0, 3);
+
+    static_assert(sv2.size() == 3, "");
+    static_assert(sv2[0] == 'A', "");
+    static_assert(sv2[1] == 'B', "");
+    static_assert(sv2[2] == 'C', "");
+  }
+
+  {
+    constexpr std::string_view sv2 = (sv.*TestSub)(3, 0);
+    static_assert(sv2.size() == 0, "");
+  }
+
+  {
+    constexpr std::string_view sv2 = (sv.*TestSub)(3, 3);
+    static_assert(sv2.size() == 2, "");
+    static_assert(sv2[0] == 'D', "");
+    static_assert(sv2[1] == 'E', "");
+  }
+}
+
+void test_constexpr() {
+  testConstexprDetail<&std::string_view::substr>();
+#  if TEST_STD_VER >= 26
+  testConstexprDetail<&std::string_view::subview>();
+#  endif
+}
+#endif
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 14
+  test_constexpr();
+#endif
+
+  return 0;
+}

>From e477951972d74bee906cb78e9380c2577efd89e7 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Fri, 4 Jul 2025 21:26:04 +0300
Subject: [PATCH 2/2] Renamed test

---
 ...ubstr_subview.pass.cpp => substr.pass.cpp} | 43 +++++++++----------
 .../string.view.ops/subview.pass.cpp          | 20 +++++++++
 2 files changed, 41 insertions(+), 22 deletions(-)
 rename libcxx/test/std/strings/string.view/string.view.ops/{substr_subview.pass.cpp => substr.pass.cpp} (77%)
 create mode 100644 libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp

diff --git a/libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
similarity index 77%
rename from libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp
rename to libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
index c40c771db4bae..02755985ead70 100644
--- a/libcxx/test/std/strings/string.view/string.view.ops/substr_subview.pass.cpp
+++ b/libcxx/test/std/strings/string.view/string.view.ops/substr.pass.cpp
@@ -34,17 +34,16 @@ struct Test {
       typename std::basic_string_view<CharT>::size_type, typename std::basic_string_view<CharT>::size_type) const;
 };
 
-template <typename CharT>
-void testDetail(std::basic_string_view<CharT> sv, typename Test<CharT>::Sub testSub, std::size_t n, size_t pos) {
-  (void)testSub; // Avoid unused parameter warning
+template <typename CharT, typename Test<CharT>::Sub TestSub>
+void testDetail(std::basic_string_view<CharT> sv, std::size_t n, size_t pos) {
   std::basic_string_view<CharT> sv1;
 #ifdef TEST_HAS_NO_EXCEPTIONS
   if (pos > sv.size())
     return; // would throw if exceptions were enabled
-  sv1 = (sv.*testSub)(pos, n);
+  sv1 = (sv.*TestSub)(pos, n);
 #else
   try {
-    sv1 = (sv.*testSub)(pos, n);
+    sv1 = (sv.*TestSub)(pos, n);
     assert(pos <= sv.size());
   } catch (const std::out_of_range&) {
     assert(pos > sv.size());
@@ -57,31 +56,31 @@ void testDetail(std::basic_string_view<CharT> sv, typename Test<CharT>::Sub test
     assert(sv[pos + i] == sv1[i]);
 }
 
-template <typename CharT>
-void testCases(std::basic_string_view<CharT> sv, typename Test<CharT>::Sub testSub) {
-  testDetail(sv, testSub, 0, 0);
-  testDetail(sv, testSub, 1, 0);
-  testDetail(sv, testSub, 20, 0);
-  testDetail(sv, testSub, sv.size(), 0);
+template <typename CharT, typename Test<CharT>::Sub TestSub>
+void testCases(const CharT* s) {
+  std::basic_string_view<CharT> sv(s);
 
-  testDetail(sv, testSub, 100, 3);
+  testDetail<CharT, TestSub>(sv, 0, 0);
+  testDetail<CharT, TestSub>(sv, 1, 0);
+  testDetail<CharT, TestSub>(sv, 20, 0);
+  testDetail<CharT, TestSub>(sv, sv.size(), 0);
 
-  testDetail(sv, testSub, 0, std::basic_string_view<CharT>::npos);
-  testDetail(sv, testSub, 2, std::basic_string_view<CharT>::npos);
-  testDetail(sv, testSub, sv.size(), std::basic_string_view<CharT>::npos);
+  testDetail<CharT, TestSub>(sv, 100, 3);
 
-  testDetail(sv, testSub, sv.size() + 1, 0);
-  testDetail(sv, testSub, sv.size() + 1, 1);
-  testDetail(sv, testSub, sv.size() + 1, std::basic_string_view<CharT>::npos);
+  testDetail<CharT, TestSub>(sv, 0, std::basic_string_view<CharT>::npos);
+  testDetail<CharT, TestSub>(sv, 2, std::basic_string_view<CharT>::npos);
+  testDetail<CharT, TestSub>(sv, sv.size(), std::basic_string_view<CharT>::npos);
+
+  testDetail<CharT, TestSub>(sv, sv.size() + 1, 0);
+  testDetail<CharT, TestSub>(sv, sv.size() + 1, 1);
+  testDetail<CharT, TestSub>(sv, sv.size() + 1, std::basic_string_view<CharT>::npos);
 }
 
 template <typename CharT>
 void testSubs(const CharT* s) {
-  std::basic_string_view<CharT> sv(s);
-
-  testCases(sv, &std::basic_string_view<CharT>::substr);
+  testCases<CharT, &std::basic_string_view<CharT>::substr>(s);
 #if TEST_STD_VER >= 26
-  testCases(sv, &std::basic_string_view<CharT>::subview);
+  testCases<CharT, &std::basic_string_view<CharT>::subview>(s);
 #endif // TEST_STD_VER >= 26
 }
 
diff --git a/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp
new file mode 100644
index 0000000000000..fec258eef4546
--- /dev/null
+++ b/libcxx/test/std/strings/string.view/string.view.ops/subview.pass.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++26
+
+// <string_view>
+
+// constexpr basic_string_view subview(size_type pos = 0,
+//                                     size_type n = npos) const;      // freestanding-deleted
+
+int main(int, char**) {
+  // This test is intentionally empty because subview is an alias for substr
+  // and is tested in substr.pass.cpp.
+  return 0;
+}



More information about the libcxx-commits mailing list