[PATCH v2] libstdc++: Export explicit instantiations for C++20 members of std::string

Jonathan Wakely jwakely.gcc@gmail.com
Tue Apr 7 15:44:10 GMT 2026


On Sat, 4 Apr 2026, 17:26 Tomasz Kamiński, <tkaminsk@redhat.com> wrote:

> The C++20 standard added new starts_with and ends_with members to
> std::basic_string, which were not previously instantiated in the library.
> This meant that the extern template declarations had to be disabled for
> C++20 mode. With this patch the new members are instantiated in the
> library and so the explicit instantiation declarations can be used for
> C++20.
>
> Furthermore, basic_string default constructor is now constrained with
> is_default_constructible_v<_Alloc> constrains, that is included in
> mangled name, so we also need to instantiate and export it.
>
> The new members added by C++23 are still not exported, and so the
> explicit instantiation declarations are still disabled for C++23.
>
> libstdc++-v3/ChangeLog:
>
>         * config/abi/pre/gnu.ver (GLIBCXX_3.4): Make string exports
>         less greedy.
>         (GLIBCXX_3.4.26): Export basic_string default constructor and
>         starts_with and ends_with members.
>         * include/bits/basic_string.h: Update __cpluplus checks for C++20.
>         * include/bits/cow_string.h: Likewise.
>         * include/bits/basic_string.tcc: Declare explicit instantiations
>         for C++20 as well as earlier dialects.
>         * src/c++20/Makefile.am: Add cow-string-inst.cc and
>         string-inst.cc source files.
>         * src/c++20/Makefile.in: Regenerate.
>         * src/c++20/string-inst.cc: New file defining explicit
>         instantiations for basic_string default constructor and
> starts_with,
>         ends_with methods added in C++20
>         * src/c++20/cow-string-inst.cc: Version of above for cow-stings.
>
> Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
> ---
> v2 updates the years in copyright and standard reference,
> as suggested.
>
> Tested after Jakub's fix for PR124754 on x86_64-linux,
> and all test passed. Same for test -O0.
> OK for trunk?
>

OK, thanks



>  libstdc++-v3/config/abi/pre/gnu.ver        | 31 ++++++++++--
>  libstdc++-v3/include/bits/basic_string.h   |  2 +-
>  libstdc++-v3/include/bits/basic_string.tcc | 17 ++++---
>  libstdc++-v3/include/bits/cow_string.h     |  4 +-
>  libstdc++-v3/src/c++20/Makefile.am         | 16 +++++-
>  libstdc++-v3/src/c++20/Makefile.in         | 15 ++++--
>  libstdc++-v3/src/c++20/cow-string-inst.cc  | 34 +++++++++++++
>  libstdc++-v3/src/c++20/string-inst.cc      | 59 ++++++++++++++++++++++
>  8 files changed, 158 insertions(+), 20 deletions(-)
>  create mode 100644 libstdc++-v3/src/c++20/cow-string-inst.cc
>  create mode 100644 libstdc++-v3/src/c++20/string-inst.cc
>
> diff --git a/libstdc++-v3/config/abi/pre/gnu.ver
> b/libstdc++-v3/config/abi/pre/gnu.ver
> index fb968e122d8..2ff2aa02383 100644
> --- a/libstdc++-v3/config/abi/pre/gnu.ver
> +++ b/libstdc++-v3/config/abi/pre/gnu.ver
> @@ -268,9 +268,11 @@ GLIBCXX_3.4 {
>      _ZNSspLE[PRc]*;
>      _ZNKSs[0-3][a-b]*;
>      _ZNKSs[5-9][a-b]*;
> -    _ZNKSs[0-9][d-e]*;
> +    _ZNKSs[0-8][d-e]*;
>      _ZNKSs[0-9][g-z]*;
> -    _ZNKSs[0-9][0-9][a-z]*;
> +    _ZNKSs11_[MS]_*;
> +    _ZNKSs1[2-8][a-z]*;
> +    _ZNKSs[2-9][0-9][a-z]*;
>      _ZNKSs4find*;
>      _ZNKSs[abd-z]*;
>      _ZNKSs4_Rep12_M_is_leakedEv;
> @@ -339,9 +341,11 @@ GLIBCXX_3.4 {
>      _ZNSbIwSt11char_traitsIwESaIwEEpLE[PRw]*;
>      _ZNKSbIwSt11char_traitsIwESaIwEE[0-3][a-b]*;
>      _ZNKSbIwSt11char_traitsIwESaIwEE[5-9][a-b]*;
> -    _ZNKSbIwSt11char_traitsIwESaIwEE[0-9][d-e]*;
> +    _ZNKSbIwSt11char_traitsIwESaIwEE[0-8][d-e]*;
>      _ZNKSbIwSt11char_traitsIwESaIwEE[0-9][g-z]*;
> -    _ZNKSbIwSt11char_traitsIwESaIwEE[0-9][0-9][a-z]*;
> +    _ZNKSbIwSt11char_traitsIwESaIwEE11_[MS]_*;
> +    _ZNKSbIwSt11char_traitsIwESaIwEE1[2-8][a-z]*;
> +    _ZNKSbIwSt11char_traitsIwESaIwEE[2-9][0-9][a-z]*;
>      _ZNKSbIwSt11char_traitsIwESaIwEE[abd-z]*;
>      _ZNKSbIwSt11char_traitsIwESaIwEE4find*;
>      _ZNKSbIwSt11char_traitsIwESaIwEE4_Rep12_M_is_leakedEv;
> @@ -1811,7 +1815,9 @@ GLIBCXX_3.4.21 {
>      _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[12]Ev;
>
>  _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[12]I[PN]*;
>      _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE[Daip]*;
> -    _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE[1-9]*;
> +
> _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE11_[MS]_*;
> +
> _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE1[2-9]*;
> +    _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE[2-8]*;
>
>  _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEixE[jmy];
>
>      # operator+ for ABI-tagged std::basic_string
> @@ -2587,6 +2593,21 @@ GLIBCXX_3.4.35 {
>      _ZNSt12__cow_stringaSEOS_;
>      _ZNKSt12__cow_string5c_strEv;
>
> +    # basic_string::starts_with
> +    _ZNKSs11starts_with*;
> +    _ZNKSbIwSt11char_traitsIwESaIwEE11starts_with*;
> +
> _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE11starts_with*;
> +
> +    # basic_string::ends_with
> +    _ZNKSs9ends_with*;
> +    _ZNKSbIwSt11char_traitsIwESaIwEE9ends_with*;
> +
> _ZNKSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE9ends_with*;
> +
> +    # basic_string::basic_string() noexcept
> is_default_constructible<Allocator>;
> +    _ZNSsC[12]EvQ26is_default_constructible_vIT1_E;
> +
> _ZNSbIwSt11char_traitsIwESaIwEEC[12]EvQ26is_default_constructible_vIT1_E;
> +
> _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[1-2]EvQ26is_default_constructible_vIT1_E;
> +
>  #if defined (_WIN32) && !defined (__CYGWIN__)
>      _ZSt19__get_once_callablev;
>      _ZSt15__get_once_callv;
> diff --git a/libstdc++-v3/include/bits/basic_string.h
> b/libstdc++-v3/include/bits/basic_string.h
> index 202a911f9ef..af4e5d9486f 100644
> --- a/libstdc++-v3/include/bits/basic_string.h
> +++ b/libstdc++-v3/include/bits/basic_string.h
> @@ -1331,7 +1331,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>        /**
>         *  Equivalent to shrink_to_fit().
>         */
> -#if __cplusplus > 201703L
> +#if __cplusplus >= 202002L
>        [[deprecated("use shrink_to_fit() instead")]]
>  #endif
>        _GLIBCXX20_CONSTEXPR
> diff --git a/libstdc++-v3/include/bits/basic_string.tcc
> b/libstdc++-v3/include/bits/basic_string.tcc
> index a223edf67ac..b00dd550237 100644
> --- a/libstdc++-v3/include/bits/basic_string.tcc
> +++ b/libstdc++-v3/include/bits/basic_string.tcc
> @@ -1031,12 +1031,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    // Inhibit implicit instantiations for required instantiations,
>    // which are defined via explicit instantiations elsewhere.
>  #if _GLIBCXX_EXTERN_TEMPLATE
> -  // The explicit instantiation definitions in src/c++11/string-inst.cc
> and
> -  // src/c++17/string-inst.cc only instantiate the members required for
> C++17
> -  // and earlier standards (so not C++20's starts_with and ends_with).
> -  // Suppress the explicit instantiation declarations for C++20, so C++20
> +  // The explicit instantiation definitions in src/c++11/string-inst.cc,
> +  // src/c++17/string-inst.cc and src/c++20/string-inst.cc only
> instantiate
> +  // the members required for C++20 and earlier standards (so not C++23's
> +  // contains).
> +  // Suppress the explicit instantiation declarations for C++23, so C++23
>    // code will implicitly instantiate std::string and std::wstring as
> needed.
> -# if __cplusplus <= 201703L && _GLIBCXX_EXTERN_TEMPLATE > 0
> +# if __cplusplus <= 202002L && _GLIBCXX_EXTERN_TEMPLATE > 0
>    extern template class basic_string<char>;
>  # elif ! _GLIBCXX_USE_CXX11_ABI
>    // Still need to prevent implicit instantiation of the COW empty rep,
> @@ -1044,7 +1045,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    extern template basic_string<char>::size_type
>      basic_string<char>::_Rep::_S_empty_rep_storage[];
>  # elif _GLIBCXX_EXTERN_TEMPLATE > 0
> -  // Export _M_replace_cold even for C++20.
> +  // Export _M_replace_cold even for C++23.
>    extern template void
>      basic_string<char>::_M_replace_cold(char *, size_type, const char*,
>                                         const size_type, const size_type);
> @@ -1064,13 +1065,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>      getline(basic_istream<char>&, string&);
>
>  #ifdef _GLIBCXX_USE_WCHAR_T
> -# if __cplusplus <= 201703L && _GLIBCXX_EXTERN_TEMPLATE > 0
> +# if __cplusplus <= 202002L && _GLIBCXX_EXTERN_TEMPLATE > 0
>    extern template class basic_string<wchar_t>;
>  # elif ! _GLIBCXX_USE_CXX11_ABI
>    extern template basic_string<wchar_t>::size_type
>      basic_string<wchar_t>::_Rep::_S_empty_rep_storage[];
>  # elif _GLIBCXX_EXTERN_TEMPLATE > 0
> -  // Export _M_replace_cold even for C++20.
> +  // Export _M_replace_cold even for C++23.
>    extern template void
>      basic_string<wchar_t>::_M_replace_cold(wchar_t*, size_type, const
> wchar_t*,
>                                            const size_type, const
> size_type);
> diff --git a/libstdc++-v3/include/bits/cow_string.h
> b/libstdc++-v3/include/bits/cow_string.h
> index df71185e556..7c945f6b998 100644
> --- a/libstdc++-v3/include/bits/cow_string.h
> +++ b/libstdc++-v3/include/bits/cow_string.h
> @@ -1080,7 +1080,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        reserve(size_type __res_arg);
>
>        /// Equivalent to shrink_to_fit().
> -#if __cplusplus > 201703L
> +#if __cplusplus >= 202002L
>        [[deprecated("use shrink_to_fit() instead")]]
>  #endif
>        void
> @@ -3194,7 +3194,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         return __r;
>        }
>
> -#if __cplusplus > 201703L
> +#if __cplusplus >= 202002L
>        bool
>        starts_with(basic_string_view<_CharT, _Traits> __x) const noexcept
>        { return __sv_type(this->data(), this->size()).starts_with(__x); }
> diff --git a/libstdc++-v3/src/c++20/Makefile.am
> b/libstdc++-v3/src/c++20/Makefile.am
> index 0061678dc0f..af5d2fa4057 100644
> --- a/libstdc++-v3/src/c++20/Makefile.am
> +++ b/libstdc++-v3/src/c++20/Makefile.am
> @@ -27,10 +27,19 @@ noinst_LTLIBRARIES = libc++20convenience.la
>
>  headers =
>
> +if ENABLE_DUAL_ABI
> +extra_string_inst_sources = cow-string-inst.cc
> +else
> +extra_string_inst_sources =
> +endif
> +
> +
>  if ENABLE_EXTERN_TEMPLATE
>  # XTEMPLATE_FLAGS = -fno-implicit-templates
>  inst_sources = \
> -       sstream-inst.cc
> +       sstream-inst.cc \
> +       string-inst.cc \
> +       $(extra_string_inst_sources)
>  else
>  # XTEMPLATE_FLAGS =
>  inst_sources =
> @@ -40,6 +49,11 @@ sources = tzdb.cc format.cc atomic.cc clock.cc
> syncbuf.cc
>
>  vpath % $(top_srcdir)/src/c++20
>
> +if ENABLE_DUAL_ABI
> +# These files should be rebuilt if the .cc prerequisite changes.
> +cow-string-inst.lo cow-string-inst.o: string-inst.cc
> +endif
> +
>  if USE_STATIC_TZDATA
>  tzdata.zi.h: $(top_srcdir)/src/c++20/tzdata.zi
>         echo 'static const char tzdata_chars[] = R"__libstdcxx__(' > $@.tmp
> diff --git a/libstdc++-v3/src/c++20/Makefile.in
> b/libstdc++-v3/src/c++20/Makefile.in
> index f481ad08edb..a2386ec7ee0 100644
> --- a/libstdc++-v3/src/c++20/Makefile.in
> +++ b/libstdc++-v3/src/c++20/Makefile.in
> @@ -124,9 +124,11 @@ CONFIG_CLEAN_VPATH_FILES =
>  LTLIBRARIES = $(noinst_LTLIBRARIES)
>  libc__20convenience_la_LIBADD =
>  am__objects_1 = tzdb.lo format.lo atomic.lo clock.lo syncbuf.lo
> -@ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_2 = sstream-inst.lo
> +@ENABLE_DUAL_ABI_TRUE@am__objects_2 = cow-string-inst.lo
> +@ENABLE_EXTERN_TEMPLATE_TRUE@am__objects_3 = sstream-inst.lo \
> +@ENABLE_EXTERN_TEMPLATE_TRUE@  string-inst.lo $(am__objects_2)
>  @GLIBCXX_HOSTED_TRUE@am_libc__20convenience_la_OBJECTS =  \
> -@GLIBCXX_HOSTED_TRUE@  $(am__objects_1) $(am__objects_2)
> +@GLIBCXX_HOSTED_TRUE@  $(am__objects_1) $(am__objects_3)
>  libc__20convenience_la_OBJECTS = $(am_libc__20convenience_la_OBJECTS)
>  AM_V_lt = $(am__v_lt_@AM_V@)
>  am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
> @@ -428,12 +430,16 @@ AM_CPPFLAGS = $(GLIBCXX_INCLUDES) $(CPPFLAGS)
>  # Convenience library for C++20 runtime.
>  noinst_LTLIBRARIES = libc++20convenience.la
>  headers =
> +@ENABLE_DUAL_ABI_FALSE@extra_string_inst_sources =
> +@ENABLE_DUAL_ABI_TRUE@extra_string_inst_sources = cow-string-inst.cc
>  # XTEMPLATE_FLAGS =
>  @ENABLE_EXTERN_TEMPLATE_FALSE@inst_sources =
>
>  # XTEMPLATE_FLAGS = -fno-implicit-templates
>  @ENABLE_EXTERN_TEMPLATE_TRUE@inst_sources = \
> -@ENABLE_EXTERN_TEMPLATE_TRUE@  sstream-inst.cc
> +@ENABLE_EXTERN_TEMPLATE_TRUE@  sstream-inst.cc \
> +@ENABLE_EXTERN_TEMPLATE_TRUE@  string-inst.cc \
> +@ENABLE_EXTERN_TEMPLATE_TRUE@  $(extra_string_inst_sources)
>
>  sources = tzdb.cc format.cc atomic.cc clock.cc syncbuf.cc
>  @GLIBCXX_HOSTED_FALSE@libc__20convenience_la_SOURCES =
> @@ -747,6 +753,9 @@ uninstall-am:
>
>  vpath % $(top_srcdir)/src/c++20
>
> +# These files should be rebuilt if the .cc prerequisite changes.
> +@ENABLE_DUAL_ABI_TRUE@cow-string-inst.lo cow-string-inst.o:
> string-inst.cc
> +
>  @USE_STATIC_TZDATA_TRUE@tzdata.zi.h: $(top_srcdir)/src/c++20/tzdata.zi
>  @USE_STATIC_TZDATA_TRUE@       echo 'static const char tzdata_chars[] =
> R"__libstdcxx__(' > $@.tmp
>  @USE_STATIC_TZDATA_TRUE@       cat $^ >> $@.tmp
> diff --git a/libstdc++-v3/src/c++20/cow-string-inst.cc
> b/libstdc++-v3/src/c++20/cow-string-inst.cc
> new file mode 100644
> index 00000000000..f3f411f5d0a
> --- /dev/null
> +++ b/libstdc++-v3/src/c++20/cow-string-inst.cc
> @@ -0,0 +1,34 @@
> +// Reference-counted COW string instantiations for C++20 -*- C++ -*-
> +
> +// Copyright (C) 2026 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +//
> +// ISO C++ 14882:2020 21  Strings library
> +//
> +
> +#define _GLIBCXX_USE_CXX11_ABI 0
> +#include "string-inst.cc"
> +
> +#if ! _GLIBCXX_USE_DUAL_ABI
> +# error This file should not be compiled for this configuration.
> +#endif
> diff --git a/libstdc++-v3/src/c++20/string-inst.cc
> b/libstdc++-v3/src/c++20/string-inst.cc
> new file mode 100644
> index 00000000000..2bdd77c78a4
> --- /dev/null
> +++ b/libstdc++-v3/src/c++20/string-inst.cc
> @@ -0,0 +1,59 @@
> +// string instantiations for C++20 -*- C++ -*-
> +
> +// Copyright (C) 2026 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library.  This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +//
> +// ISO C++ 14882:2020 21  Strings library
> +//
> +
> +#ifndef _GLIBCXX_USE_CXX11_ABI
> +// Instantiations in this file use the new SSO std::string ABI unless
> included
> +// by another file which defines _GLIBCXX_USE_CXX11_ABI=0.
> +# define _GLIBCXX_USE_CXX11_ABI 1
> +#endif
> +
> +#include <string>
> +
> +namespace std _GLIBCXX_VISIBILITY(default)
> +{
> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> +template basic_string<char>::basic_string() noexcept;
> +template bool basic_string<char>::starts_with(string_view) const noexcept;
> +template bool basic_string<char>::starts_with(char) const noexcept;
> +template bool basic_string<char>::starts_with(const char*) const noexcept;
> +template bool basic_string<char>::ends_with(string_view) const noexcept;
> +template bool basic_string<char>::ends_with(char) const noexcept;
> +template bool basic_string<char>::ends_with(const char*) const noexcept;
> +
> +#ifdef _GLIBCXX_USE_WCHAR_T
> +template basic_string<wchar_t>::basic_string() noexcept;
> +template bool basic_string<wchar_t>::starts_with(wstring_view) const
> noexcept;
> +template bool basic_string<wchar_t>::starts_with(wchar_t) const noexcept;
> +template bool basic_string<wchar_t>::starts_with(const wchar_t*) const
> noexcept;
> +template bool basic_string<wchar_t>::ends_with(wstring_view) const
> noexcept;
> +template bool basic_string<wchar_t>::ends_with(wchar_t) const noexcept;
> +template bool basic_string<wchar_t>::ends_with(const wchar_t*) const
> noexcept;
> +#endif
> +
> +_GLIBCXX_END_NAMESPACE_VERSION
> +} // namespace std
> --
> 2.53.0
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260407/841db346/attachment-0001.htm>


More information about the Libstdc++ mailing list