On Fri, 21 Nov 2025 at 01:50 +0800, Yuao Ma wrote:
Hi all,

This patch implements P3044R2 by adding a subview function for string
and string_view. The implementation delegates directly to substr.
Tested on x86_64-linux.
Also, could you please help review my other patch regarding P3223R2 at
https://gcc.gnu.org/pipermail/libstdc++/2025-November/064369.html?

Thanks,
Yuao

From 6cf50b0e5a2fc1ca6814dbc4555c543c690d300a Mon Sep 17 00:00:00 2001
From: Yuao Ma <[email protected]>
Date: Fri, 21 Nov 2025 01:42:51 +0800
Subject: [PATCH] libstdc++: implement P3044R2 - sub-string_view from string

libstdc++-v3/ChangeLog:

        * include/bits/basic_string.h: Add subview.
        * include/bits/version.def: Add string_subview FTM.
        * include/bits/version.h: Regenerate.
        * include/std/string: Add FTM.
        * include/std/string_view: Add subview.
        * testsuite/21_strings/basic_string/operations/subview/char.cc: New 
test.
        * testsuite/21_strings/basic_string/operations/subview/wchar_t.cc: New 
test.
        * testsuite/21_strings/basic_string_view/operations/subview/char.cc: 
New test.
        * testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc: 
New test.
---
libstdc++-v3/include/bits/basic_string.h      | 19 +++++++
libstdc++-v3/include/bits/version.def         |  9 ++++
libstdc++-v3/include/bits/version.h           | 10 ++++
libstdc++-v3/include/std/string               |  1 +
libstdc++-v3/include/std/string_view          | 10 +++-
.../basic_string/operations/subview/char.cc   | 46 +++++++++++++++++
.../operations/subview/wchar_t.cc             | 46 +++++++++++++++++
.../operations/subview/char.cc                | 50 ++++++++++++++++++
.../operations/subview/wchar_t.cc             | 51 +++++++++++++++++++
9 files changed, 241 insertions(+), 1 deletion(-)
create mode 100644 
libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc
create mode 100644 
libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc
create mode 100644 
libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
create mode 100644 
libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc

diff --git a/libstdc++-v3/include/bits/basic_string.h 
b/libstdc++-v3/include/bits/basic_string.h
index c4b6b1064a9..e87eb93e286 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -3442,6 +3442,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
      { return basic_string(*this,
                            _M_check(__pos, "basic_string::substr"), __n); }

+#ifdef __glibcxx_string_subview // >= C++26
+      /**
+       *  @brief  Get a subview.
+       *  @param __pos  Index of first character (default 0).
+       *  @param __n  Number of characters in subview (default remainder).
+       *  @return  The subview.
+       *  @throw  std::out_of_range  If __pos > size().
+       *
+       *  Construct and return a subview using the @a __n characters starting

Also, we can use markdown formatting instead of Doxygen commands, so
`__pos` instead of @a __pos.

+       *  at @a __pos.  If the string is too short, use the remainder of the
+       *  characters.  If @a __pos is beyond the end of the string,
+       *  out_of_range is thrown.
+      */
+      _GLIBCXX_NODISCARD constexpr
+      basic_string_view<_CharT, _Traits>
+      subview(size_type __pos = 0, size_type __n = npos) const
+      { return __sv_type(*this).subview(__pos, __n); }
+#endif
+
      /**
       *  @brief  Compare to a string.
       *  @param __str  String to compare against.
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 29ecf15c7e3..b5575d2399f 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1934,6 +1934,15 @@ ftms = {
  };
};

+ftms = {
+  name = string_subview;
+  values = {
+    v = 202506;
+    cxxmin = 26;
+    hosted = yes;
+  };
+};
+
ftms = {
  name = to_underlying;
  values = {
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 5901d27113d..413da56b088 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2161,6 +2161,16 @@
#endif /* !defined(__cpp_lib_string_resize_and_overwrite) */
#undef __glibcxx_want_string_resize_and_overwrite

+#if !defined(__cpp_lib_string_subview)
+# if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
+#  define __glibcxx_string_subview 202506L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_string_subview)
+#   define __cpp_lib_string_subview 202506L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_string_subview) */
+#undef __glibcxx_want_string_subview
+
#if !defined(__cpp_lib_to_underlying)
# if (__cplusplus >= 202100L)
#  define __glibcxx_to_underlying 202102L
diff --git a/libstdc++-v3/include/std/string b/libstdc++-v3/include/std/string
index 97ded057a87..918b4158b47 100644
--- a/libstdc++-v3/include/std/string
+++ b/libstdc++-v3/include/std/string
@@ -63,6 +63,7 @@
#define __glibcxx_want_erase_if
#define __glibcxx_want_nonmember_container_access
#define __glibcxx_want_string_resize_and_overwrite
+#define __glibcxx_want_string_subview
#define __glibcxx_want_string_udls
#define __glibcxx_want_to_string
#include <bits/version.h>
diff --git a/libstdc++-v3/include/std/string_view 
b/libstdc++-v3/include/std/string_view
index 842f6ad89af..b226544fa6f 100644
--- a/libstdc++-v3/include/std/string_view
+++ b/libstdc++-v3/include/std/string_view
@@ -40,9 +40,10 @@
#define __glibcxx_want_constexpr_char_traits
#define __glibcxx_want_constexpr_string_view
#define __glibcxx_want_freestanding_string_view
-#define __glibcxx_want_string_view
#define __glibcxx_want_starts_ends_with
#define __glibcxx_want_string_contains
+#define __glibcxx_want_string_subview
+#define __glibcxx_want_string_view
#include <bits/version.h>

#if __cplusplus >= 201703L
@@ -342,6 +343,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        return basic_string_view{_M_str + __pos, __rlen};
      }

+#ifdef __glibcxx_string_subview // >= C++26
+      [[nodiscard]]
+      constexpr basic_string_view
+      subview(size_type __pos = 0, size_type __n = npos) const
+      { return substr(__pos, __n); }
+#endif
+
      [[nodiscard]]
      constexpr int
      compare(basic_string_view __str) const noexcept
diff --git 
a/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc 
b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc
new file mode 100644
index 00000000000..c384948cc52
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc
@@ -0,0 +1,46 @@
+// { dg-do run { target c++26 } }
+
+#include <stdexcept>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+
+void test01(void) {
+  typedef std::string::size_type csize_type;
+  typedef std::string::const_reference cref;
+  typedef std::string::reference ref;
+  csize_type csz01;
+
+  const char str_lit01[] = "rockaway, pacifica";
+  const std::string str01(str_lit01);
+  std::string_view str02;
+
+  csz01 = str01.size();
+  str02 = str01.subview(0, 1);
+  VERIFY(str02 == "r");
+  str02 = str01.subview(10);
+  VERIFY(str02 == "pacifica");
+
+  try {
+    str02 = str01.subview(csz01 + 1);
+    VERIFY(false);
+  } catch (std::out_of_range &fail) {
+    VERIFY(true);
+  } catch (...) {
+    VERIFY(false);
+  }
+
+  try {
+    str02 = str01.subview(csz01);
+    VERIFY(str02.size() == 0);
+  } catch (std::out_of_range &fail) {
+    VERIFY(false);
+  } catch (...) {
+    VERIFY(false);
+  }
+}
+
+int main() {
+  test01();
+  return 0;
+}
diff --git 
a/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc 
b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc
new file mode 100644
index 00000000000..3b8e6a87fe6
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc
@@ -0,0 +1,46 @@
+// { dg-do run { target c++26 } }
+
+#include <stdexcept>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+
+void test01(void) {
+  typedef std::wstring::size_type csize_type;
+  typedef std::wstring::const_reference cref;
+  typedef std::wstring::reference ref;
+  csize_type csz01;
+
+  const wchar_t str_lit01[] = L"rockaway, pacifica";
+  const std::wstring str01(str_lit01);
+  std::wstring_view str02;
+
+  csz01 = str01.size();
+  str02 = str01.subview(0, 1);
+  VERIFY(str02 == L"r");
+  str02 = str01.subview(10);
+  VERIFY(str02 == L"pacifica");
+
+  try {
+    str02 = str01.subview(csz01 + 1);
+    VERIFY(false);
+  } catch (std::out_of_range &fail) {
+    VERIFY(true);
+  } catch (...) {
+    VERIFY(false);
+  }
+
+  try {
+    str02 = str01.subview(csz01);
+    VERIFY(str02.size() == 0);
+  } catch (std::out_of_range &fail) {
+    VERIFY(false);
+  } catch (...) {
+    VERIFY(false);
+  }
+}
+
+int main() {
+  test01();
+  return 0;
+}
diff --git 
a/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
 
b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
new file mode 100644
index 00000000000..f0ce145b45b
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc
@@ -0,0 +1,50 @@
+// { dg-do run { target c++26 } }
+#include <string_view>
+#include <testsuite_hooks.h>
+
+#if __STDC_HOSTED__
+#include <stdexcept>
+#endif
+
+void test01() {
+  typedef std::string_view::size_type csize_type;
+  typedef std::string_view::const_reference cref;
+  typedef std::string_view::reference ref;
+  csize_type csz01;
+
+  const char str_lit01[] = "rockaway, pacifica";
+  const std::string_view str01(str_lit01);
+  std::string_view str02;
+
+  csz01 = str01.size();
+  str02 = str01.subview(0, 1);
+  VERIFY(str02 == "r");
+  str02 = str01.subview(10);
+  VERIFY(str02 == "pacifica");
+
+#if __STDC_HOSTED__
+  try {
+    str02 = str01.subview(csz01 + 1);
+    VERIFY(false);
+  } catch (std::out_of_range &fail) {
+    VERIFY(true);
+  } catch (...) {
+    VERIFY(false);
+  }
+
+  try {
+    str02 = str01.subview(csz01);
+    VERIFY(str02.size() == 0);
+    VERIFY(str02.begin() == str01.end());
+    VERIFY(true);
+  } catch (...) {
+    VERIFY(false);
+  }
+#endif // HOSTED
+}
+
+int main() {
+  test01();
+
+  return 0;
+}
diff --git 
a/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc
 
b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc
new file mode 100644
index 00000000000..86b50959b5c
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc
@@ -0,0 +1,51 @@
+// { dg-do run { target c++26 } }
+
+#include <string_view>
+#include <testsuite_hooks.h>
+
+#if __STDC_HOSTED__
+#include <stdexcept>
+#endif
+
+void test01() {
+  typedef std::wstring_view::size_type csize_type;
+  typedef std::wstring_view::const_reference cref;
+  typedef std::wstring_view::reference ref;
+  csize_type csz01;
+
+  const wchar_t str_lit01[] = L"rockaway, pacifica";
+  const std::wstring_view str01(str_lit01);
+  std::wstring_view str02;
+
+  csz01 = str01.size();
+  str02 = str01.subview(0, 1);
+  VERIFY(str02 == L"r");
+  str02 = str01.subview(10);
+  VERIFY(str02 == L"pacifica");
+
+#if __STDC_HOSTED__
+  try {
+    str02 = str01.subview(csz01 + 1);
+    VERIFY(false);
+  } catch (std::out_of_range &fail) {
+    VERIFY(true);
+  } catch (...) {
+    VERIFY(false);
+  }
+
+  try {
+    str02 = str01.subview(csz01);
+    VERIFY(str02.size() == 0);
+    VERIFY(str02.begin() == str01.end());
+    VERIFY(true);
+  } catch (...) {
+    VERIFY(false);
+  }
+#endif // HOSTED
+}
+
+int main() {
+  test01();
+
+  return 0;
+}
--
2.51.1


Reply via email to