[PATCH v3] libstdc++: mark integer std::to_(w)string constexpr
Tomasz Kaminski
tkaminsk@redhat.com
Tue Jun 2 06:47:52 GMT 2026
Hi Ivan,
The patch was just merged for the GCC-17.
Thank you for your contributions.
On Mon, Jun 1, 2026 at 5:16 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> On Mon, 1 Jun 2026 at 16:10, Tomasz Kamiński <tkaminsk@redhat.com> wrote:
> >
> > From: Ivan Lazaric <ivan.lazaric1@gmail.com>
> >
> > In C++26 paper P3391, "constexpr formatting", has been adopted,
> > part of which marks std::to_string & std::to_wstring for integers
> > as constexpr. The __cpp_lib_constexpr_string FTM value is updated
> > per resolution of LWG4531, "Should there be a feature-test macro
> > update for constexpr std::to_(w)string?".
> >
> > Since pre-cxx11 copy-on-write string is not constexpr-enabled,
> > restricting this constexpr-ification to cxx11 ABI strings.
> >
> > libstdc++-v3/ChangeLog:
> >
> > * include/bits/version.def (constexpr_string): Bump to 202511.
> > * include/bits/versin.h: Regenerate.
> > * include/bits/basic_string.h (std::to_string, std::to_wstring)
> > [__glibcxx_constexpr_string >= 202511L]: Mark as constexpr.
> > *
> testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc:
> > New test.
> > *
> testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc:
> > New test.
> > * testsuite/21_strings/basic_string/cons/char/constexpr.cc:
> Update
> > __cpp_lib_constexpr_string check.
> > testsuite/21_strings/basic_string/cons/wchar_t/constexpr.c:
> Likewise.
> > * 21_strings/basic_string/version.cc: Add check for value of
> > __cpp_lib_constexpr_string in C++26.
> >
> > Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
> > ---
> > v3 introduces _GLIBCXX_TO_STRING_CONSTEXPR as suggested by
> > Nathan.
>
> OK, thanks
>
>
> >
> > libstdc++-v3/include/bits/basic_string.h | 31 +++++----
> > libstdc++-v3/include/bits/version.def | 8 +++
> > libstdc++-v3/include/bits/version.h | 7 +-
> > .../basic_string/cons/char/constexpr.cc | 4 +-
> > .../basic_string/cons/wchar_t/constexpr.cc | 4 +-
> > .../char/to_string_constexpr.cc | 64 +++++++++++++++++++
> > .../wchar_t/to_wstring_constexpr.cc | 64 +++++++++++++++++++
> > .../21_strings/basic_string/version.cc | 12 ++++
> > 8 files changed, 177 insertions(+), 17 deletions(-)
> > create mode 100644
> libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
> > create mode 100644
> libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
> >
> > diff --git a/libstdc++-v3/include/bits/basic_string.h
> b/libstdc++-v3/include/bits/basic_string.h
> > index 23e6979f03d..65d92ebfbcf 100644
> > --- a/libstdc++-v3/include/bits/basic_string.h
> > +++ b/libstdc++-v3/include/bits/basic_string.h
> > @@ -4607,11 +4607,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > { return std::stod(__str, __idx); }
> > #endif
> >
> > +#if __glibcxx_constexpr_string >= 202511L
> > +# define _GLIBCXX_TO_STRING_CONSTEXPR constexpr
> > +#else
> > +# define _GLIBCXX_TO_STRING_CONSTEXPR inline
> > +#endif
> > +
> > // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > // DR 1261. Insufficient overloads for to_string / to_wstring
> >
> > _GLIBCXX_NODISCARD
> > - inline string
> > + _GLIBCXX_TO_STRING_CONSTEXPR string
> > to_string(int __val)
> > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32
> > noexcept // any 32-bit value fits in the SSO buffer
> > @@ -4630,7 +4636,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > }
> >
> > _GLIBCXX_NODISCARD
> > - inline string
> > + _GLIBCXX_TO_STRING_CONSTEXPR string
> > to_string(unsigned __val)
> > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32
> > noexcept // any 32-bit value fits in the SSO buffer
> > @@ -4646,7 +4652,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > }
> >
> > _GLIBCXX_NODISCARD
> > - inline string
> > + _GLIBCXX_TO_STRING_CONSTEXPR string
> > to_string(long __val)
> > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32
> > noexcept // any 32-bit value fits in the SSO buffer
> > @@ -4665,7 +4671,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > }
> >
> > _GLIBCXX_NODISCARD
> > - inline string
> > + _GLIBCXX_TO_STRING_CONSTEXPR string
> > to_string(unsigned long __val)
> > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32
> > noexcept // any 32-bit value fits in the SSO buffer
> > @@ -4681,7 +4687,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > }
> >
> > _GLIBCXX_NODISCARD
> > - inline string
> > + _GLIBCXX_TO_STRING_CONSTEXPR string
> > to_string(long long __val)
> > {
> > const bool __neg = __val < 0;
> > @@ -4698,7 +4704,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > }
> >
> > _GLIBCXX_NODISCARD
> > - inline string
> > + _GLIBCXX_TO_STRING_CONSTEXPR string
> > to_string(unsigned long long __val)
> > {
> > const auto __len = __detail::__to_chars_len(__val);
> > @@ -4922,32 +4928,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > #pragma GCC diagnostic pop
> >
> > _GLIBCXX_NODISCARD
> > - inline wstring
> > + _GLIBCXX_TO_STRING_CONSTEXPR wstring
> > to_wstring(int __val)
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> >
> > _GLIBCXX_NODISCARD
> > - inline wstring
> > + _GLIBCXX_TO_STRING_CONSTEXPR wstring
> > to_wstring(unsigned __val)
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> >
> > _GLIBCXX_NODISCARD
> > - inline wstring
> > + _GLIBCXX_TO_STRING_CONSTEXPR wstring
> > to_wstring(long __val)
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> >
> > _GLIBCXX_NODISCARD
> > - inline wstring
> > + _GLIBCXX_TO_STRING_CONSTEXPR wstring
> > to_wstring(unsigned long __val)
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> >
> > _GLIBCXX_NODISCARD
> > - inline wstring
> > + _GLIBCXX_TO_STRING_CONSTEXPR wstring
> > to_wstring(long long __val)
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> >
> > _GLIBCXX_NODISCARD
> > - inline wstring
> > + _GLIBCXX_TO_STRING_CONSTEXPR wstring
> > to_wstring(unsigned long long __val)
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> >
> > @@ -4968,6 +4974,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> > { return std::__to_wstring_numeric(std::to_string(__val)); }
> > #endif
> > #endif // _GLIBCXX_USE_WCHAR_T
> > +#undef _GLIBCXX_TO_STRING_CONSTEXPR
> >
> > _GLIBCXX_END_NAMESPACE_CXX11
> > _GLIBCXX_END_NAMESPACE_VERSION
> > diff --git a/libstdc++-v3/include/bits/version.def
> b/libstdc++-v3/include/bits/version.def
> > index 97f7d686519..ea8560778e5 100644
> > --- a/libstdc++-v3/include/bits/version.def
> > +++ b/libstdc++-v3/include/bits/version.def
> > @@ -1398,6 +1398,14 @@ ftms = {
> >
> > ftms = {
> > name = constexpr_string;
> > + // 202511 LWG4531 Should there be a feature-test macro update for
> constexpr std::to_(w)string?
> > + // P3391R2 constexpr std::format
> > + values = {
> > + v = 202511;
> > + cxxmin = 26;
> > + hosted = yes;
> > + cxx11abi = yes;
> > + };
> > values = {
> > v = 201907;
> > cxxmin = 20;
> > diff --git a/libstdc++-v3/include/bits/version.h
> b/libstdc++-v3/include/bits/version.h
> > index 33eecbfb83b..59d5d9217d7 100644
> > --- a/libstdc++-v3/include/bits/version.h
> > +++ b/libstdc++-v3/include/bits/version.h
> > @@ -1542,7 +1542,12 @@
> > #undef __glibcxx_want_constexpr_flat_set
> >
> > #if !defined(__cpp_lib_constexpr_string)
> > -# if (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED && (defined(__glibcxx_is_constant_evaluated))
> > +# if (__cplusplus > 202302L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED
> > +# define __glibcxx_constexpr_string 202511L
> > +# if defined(__glibcxx_want_all) ||
> defined(__glibcxx_want_constexpr_string)
> > +# define __cpp_lib_constexpr_string 202511L
> > +# endif
> > +# elif (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI &&
> _GLIBCXX_HOSTED && (defined(__glibcxx_is_constant_evaluated))
> > # define __glibcxx_constexpr_string 201907L
> > # if defined(__glibcxx_want_all) ||
> defined(__glibcxx_want_constexpr_string)
> > # define __cpp_lib_constexpr_string 201907L
> > diff --git
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> > index 7822c89497b..34abfe60bc5 100644
> > ---
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> > +++
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc
> > @@ -6,8 +6,8 @@
> >
> > #ifndef __cpp_lib_constexpr_string
> > # error "Feature-test macro for constexpr std::string missing in
> <string>"
> > -#elif __cpp_lib_constexpr_string != 201907L
> > -# error "Feature-test macro for constexpr std::string has wrong value
> in <string>"
> > +#elif __cpp_lib_constexpr_string < 201907L
> > +# error "Feature-test macro for constexpr std::string has too small
> value in <string>"
> > #endif
> >
> > #include <testsuite_hooks.h>
> > diff --git
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> > index 44c8391ebc2..59d5ee270cc 100644
> > ---
> a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> > +++
> b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc
> > @@ -6,8 +6,8 @@
> >
> > #ifndef __cpp_lib_constexpr_string
> > # error "Feature-test macro for constexpr std::string missing in
> <string>"
> > -#elif __cpp_lib_constexpr_string != 201907L
> > -# error "Feature-test macro for constexpr std::string has wrong value
> in <string>"
> > +#elif __cpp_lib_constexpr_string < 201907L
> > +# error "Feature-test macro for constexpr std::string too small value
> in <string>"
> > #endif
> >
> > #include <testsuite_hooks.h>
> > diff --git
> a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
> > new file mode 100644
> > index 00000000000..1b5a616df54
> > --- /dev/null
> > +++
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc
> > @@ -0,0 +1,64 @@
> > +// { dg-do compile { target c++26 } }
> > +// { dg-require-effective-target cxx11_abi }
> > +
> > +#include <string>
> > +#include <type_traits>
> > +#include <testsuite_hooks.h>
> > +
> > +template<typename T>
> > +constexpr void
> > +test()
> > +{
> > + using namespace std;
> > + string res;
> > + T value;
> > +
> > + value = 0;
> > + res = to_string(value);
> > + VERIFY( res == "0" );
> > +
> > + value = 1;
> > + res = to_string(value);
> > + VERIFY( res == "1" );
> > +
> > + value = 10;
> > + res = to_string(value);
> > + VERIFY( res == "10" );
> > +
> > + value = 3000;
> > + res = to_string(value);
> > + VERIFY( res == "3000" );
> > +
> > + value = 32767;
> > + res = to_string(value);
> > + VERIFY( res == "32767" );
> > +
> > + if (is_unsigned_v<T>)
> > + return;
> > +
> > + value = -1;
> > + res = to_string(value);
> > + VERIFY( res == "-1" );
> > +
> > + value = -40;
> > + res = to_string(value);
> > + VERIFY( res == "-40" );
> > +
> > + value = -32768;
> > + res = to_string(value);
> > + VERIFY( res == "-32768" );
> > +}
> > +
> > +constexpr bool
> > +test_all()
> > +{
> > + test<int>();
> > + test<unsigned int>();
> > + test<long>();
> > + test<unsigned long>();
> > + test<long long>();
> > + test<unsigned long long>();
> > + return true;
> > +}
> > +
> > +static_assert(test_all());
> > diff --git
> a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
> > new file mode 100644
> > index 00000000000..f884fc61ff5
> > --- /dev/null
> > +++
> b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc
> > @@ -0,0 +1,64 @@
> > +// { dg-do compile { target c++26 } }
> > +// { dg-require-effective-target cxx11_abi }
> > +
> > +#include <string>
> > +#include <type_traits>
> > +#include <testsuite_hooks.h>
> > +
> > +template<typename T>
> > +constexpr void
> > +test()
> > +{
> > + using namespace std;
> > + wstring res;
> > + T value;
> > +
> > + value = 0;
> > + res = to_wstring(value);
> > + VERIFY( res == L"0" );
> > +
> > + value = 1;
> > + res = to_wstring(value);
> > + VERIFY( res == L"1" );
> > +
> > + value = 10;
> > + res = to_wstring(value);
> > + VERIFY( res == L"10" );
> > +
> > + value = 3000;
> > + res = to_wstring(value);
> > + VERIFY( res == L"3000" );
> > +
> > + value = 32767;
> > + res = to_wstring(value);
> > + VERIFY( res == L"32767" );
> > +
> > + if (is_unsigned_v<T>)
> > + return;
> > +
> > + value = -1;
> > + res = to_wstring(value);
> > + VERIFY( res == L"-1" );
> > +
> > + value = -40;
> > + res = to_wstring(value);
> > + VERIFY( res == L"-40" );
> > +
> > + value = -32768;
> > + res = to_wstring(value);
> > + VERIFY( res == L"-32768" );
> > +}
> > +
> > +constexpr bool
> > +test_all()
> > +{
> > + test<int>();
> > + test<unsigned int>();
> > + test<long>();
> > + test<unsigned long>();
> > + test<long long>();
> > + test<unsigned long long>();
> > + return true;
> > +}
> > +
> > +static_assert(test_all());
> > diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> b/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> > index 71dd4dfb67f..b0cdf04274e 100644
> > --- a/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> > +++ b/libstdc++-v3/testsuite/21_strings/basic_string/version.cc
> > @@ -25,3 +25,15 @@
> > # endif
> > # endif
> > #endif
> > +
> > +#if __cplusplus > 202302L
> > +# if _GLIBCXX_USE_CXX11_ABI
> > +# if __cpp_lib_constexpr_string != 202511L
> > +# error "Feature-test macro for constexpr std::string has wrong value
> for C++20 in <version>"
> > +# endif
> > +# else // COW strings
> > +# if __cpp_lib_constexpr_string != 201811L
> > +# error "Feature-test macro for constexpr std::string has wrong value
> for C++20 in <version>"
> > +# endif
> > +# endif
> > +#endif
> > --
> > 2.54.0
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260602/3cace815/attachment-0001.htm>
More information about the Libstdc++
mailing list