On Sun, 16 Feb 2025, Giuseppe D'Angelo wrote: > Hello, > > the attached patch implements the C++26 papers that add `constexpr` to the > specialized memory algorithms (the uninitialized_* family). Tested on x86-64 > Linux. > > Thank you, > -- > Giuseppe D'Angelo >
> Subject: [PATCH] libstdc++: implement constexpr memory algorithms > > This commit adds support for C++26's constexpr specialized memory > algorithms, introduced by P2283R2, P3508R0, P3369R0. > > The uninitialized_default, value, copy, move and fill algorithms are > affected, in all of their variants (iterator-based, range-based and _n > versions.) > > The changes are mostly mechanical -- add `constexpr` to a number of > signatures. I've introduced a helper macro to conditionally expand to > `constexpr` only in C++26 and above modes. The internal helper guard > class for range algorithms instead can be marked unconditionally. > > uninitialized_fill is the only algorithm where I had to add a branch to > a constexpr-friendly version (already existing). Seems the patch also adds code to uninitialized_copy and uninitialized_fill_n? > > For each algorithm family I've added only one test to cover it and its > variants; the idea is to avoid too much repetition and simplify future > maintenance. > > libstdc++-v3/ChangeLog: > > * include/bits/ranges_uninitialized.h: Mark the specialized > memory algorithms as constexpr in C++26. Also mark the members > of the _DestroyGuard helper class. > * include/bits/stl_uninitialized.h: Ditto. > * include/bits/stl_construct.h: Mark _Construct_novalue (which > uses placement new to do default initialization) as constexpr > in C++26. This is possible due to P2747R2, which GCC already > implements; check P2747's feature-testing macro to avoid > issues with other compilers. > * include/bits/version.def: Bump the feature-testing macro. > * include/bits/version.h: Regenerate. > * testsuite/20_util/specialized_algorithms/feature_test_macro.cc: New > test. > * > testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc: New > test. > * > testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc: > New test. > * > testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc: New > test. > * > testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc: New > test. > * > testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc: > New test. > > Signed-off-by: Giuseppe D'Angelo <giuseppe.dang...@kdab.com> > --- > .../include/bits/ranges_uninitialized.h | 29 ++++++++ > libstdc++-v3/include/bits/stl_construct.h | 3 + > libstdc++-v3/include/bits/stl_uninitialized.h | 42 ++++++++++++ > libstdc++-v3/include/bits/version.def | 5 ++ > libstdc++-v3/include/bits/version.h | 7 +- > .../feature_test_macro.cc | 14 ++++ > .../uninitialized_copy/constexpr.cc | 58 ++++++++++++++++ > .../constexpr.cc | 67 ++++++++++++++++++ > .../uninitialized_fill/constexpr.cc | 68 +++++++++++++++++++ > .../uninitialized_move/constexpr.cc | 51 ++++++++++++++ > .../constexpr.cc | 64 +++++++++++++++++ > 11 files changed, 407 insertions(+), 1 deletion(-) > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc > > diff --git a/libstdc++-v3/include/bits/ranges_uninitialized.h > b/libstdc++-v3/include/bits/ranges_uninitialized.h > index ced7bda5e37..337d321702d 100644 > --- a/libstdc++-v3/include/bits/ranges_uninitialized.h > +++ b/libstdc++-v3/include/bits/ranges_uninitialized.h > @@ -35,6 +35,12 @@ > > #include <bits/ranges_algobase.h> > > +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26 > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS constexpr > +#else > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > +#endif > + > namespace std _GLIBCXX_VISIBILITY(default) > { > _GLIBCXX_BEGIN_NAMESPACE_VERSION > @@ -105,15 +111,18 @@ namespace ranges > const _Iter* _M_cur; > > public: > + constexpr > explicit > _DestroyGuard(const _Iter& __iter) > : _M_first(__iter), _M_cur(std::__addressof(__iter)) > { } > > + constexpr > void > release() noexcept > { _M_cur = nullptr; } > > + constexpr > ~_DestroyGuard() > { > if (_M_cur != nullptr) > @@ -126,10 +135,12 @@ namespace ranges > && is_trivially_destructible_v<iter_value_t<_Iter>> > struct _DestroyGuard<_Iter> > { > + constexpr > explicit > _DestroyGuard(const _Iter&) > { } > > + constexpr > void > release() noexcept > { } > @@ -141,6 +152,7 @@ namespace ranges > template<__detail::__nothrow_forward_iterator _Iter, > __detail::__nothrow_sentinel<_Iter> _Sent> > requires default_initializable<iter_value_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _Iter > operator()(_Iter __first, _Sent __last) const > { > @@ -159,6 +171,7 @@ namespace ranges > > template<__detail::__nothrow_forward_range _Range> > requires default_initializable<range_value_t<_Range>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > borrowed_iterator_t<_Range> > operator()(_Range&& __r) const > { > @@ -173,6 +186,7 @@ namespace ranges > { > template<__detail::__nothrow_forward_iterator _Iter> > requires default_initializable<iter_value_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _Iter > operator()(_Iter __first, iter_difference_t<_Iter> __n) const > { > @@ -198,6 +212,7 @@ namespace ranges > template<__detail::__nothrow_forward_iterator _Iter, > __detail::__nothrow_sentinel<_Iter> _Sent> > requires default_initializable<iter_value_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _Iter > operator()(_Iter __first, _Sent __last) const > { > @@ -217,6 +232,7 @@ namespace ranges > > template<__detail::__nothrow_forward_range _Range> > requires default_initializable<range_value_t<_Range>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > borrowed_iterator_t<_Range> > operator()(_Range&& __r) const > { > @@ -231,6 +247,7 @@ namespace ranges > { > template<__detail::__nothrow_forward_iterator _Iter> > requires default_initializable<iter_value_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _Iter > operator()(_Iter __first, iter_difference_t<_Iter> __n) const > { > @@ -261,6 +278,7 @@ namespace ranges > __detail::__nothrow_forward_iterator _Out, > __detail::__nothrow_sentinel<_Out> _OSent> > requires constructible_from<iter_value_t<_Out>, > iter_reference_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > uninitialized_copy_result<_Iter, _Out> > operator()(_Iter __ifirst, _ISent __ilast, > _Out __ofirst, _OSent __olast) const > @@ -292,6 +310,7 @@ namespace ranges > template<input_range _IRange, __detail::__nothrow_forward_range _ORange> > requires constructible_from<range_value_t<_ORange>, > range_reference_t<_IRange>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > uninitialized_copy_result<borrowed_iterator_t<_IRange>, > borrowed_iterator_t<_ORange>> > operator()(_IRange&& __inr, _ORange&& __outr) const > @@ -311,6 +330,7 @@ namespace ranges > template<input_iterator _Iter, __detail::__nothrow_forward_iterator _Out, > __detail::__nothrow_sentinel<_Out> _Sent> > requires constructible_from<iter_value_t<_Out>, > iter_reference_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > uninitialized_copy_n_result<_Iter, _Out> > operator()(_Iter __ifirst, iter_difference_t<_Iter> __n, > _Out __ofirst, _Sent __olast) const > @@ -350,6 +370,7 @@ namespace ranges > __detail::__nothrow_sentinel<_Out> _OSent> > requires constructible_from<iter_value_t<_Out>, > iter_rvalue_reference_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > uninitialized_move_result<_Iter, _Out> > operator()(_Iter __ifirst, _ISent __ilast, > _Out __ofirst, _OSent __olast) const > @@ -384,6 +405,7 @@ namespace ranges > template<input_range _IRange, __detail::__nothrow_forward_range _ORange> > requires constructible_from<range_value_t<_ORange>, > range_rvalue_reference_t<_IRange>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > uninitialized_move_result<borrowed_iterator_t<_IRange>, > borrowed_iterator_t<_ORange>> > operator()(_IRange&& __inr, _ORange&& __outr) const > @@ -404,6 +426,7 @@ namespace ranges > __detail::__nothrow_sentinel<_Out> _Sent> > requires constructible_from<iter_value_t<_Out>, > iter_rvalue_reference_t<_Iter>> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > uninitialized_move_n_result<_Iter, _Out> > operator()(_Iter __ifirst, iter_difference_t<_Iter> __n, > _Out __ofirst, _Sent __olast) const > @@ -441,6 +464,7 @@ namespace ranges > template<__detail::__nothrow_forward_iterator _Iter, > __detail::__nothrow_sentinel<_Iter> _Sent, typename _Tp> > requires constructible_from<iter_value_t<_Iter>, const _Tp&> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _Iter > operator()(_Iter __first, _Sent __last, const _Tp& __x) const > { > @@ -460,6 +484,7 @@ namespace ranges > > template<__detail::__nothrow_forward_range _Range, typename _Tp> > requires constructible_from<range_value_t<_Range>, const _Tp&> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > borrowed_iterator_t<_Range> > operator()(_Range&& __r, const _Tp& __x) const > { > @@ -473,6 +498,7 @@ namespace ranges > { > template<__detail::__nothrow_forward_iterator _Iter, typename _Tp> > requires constructible_from<iter_value_t<_Iter>, const _Tp&> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _Iter > operator()(_Iter __first, iter_difference_t<_Iter> __n, > const _Tp& __x) const > @@ -573,6 +599,9 @@ namespace ranges > } > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > + > +#undef _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > + > #endif // concepts > #endif // C++20 > #endif // _RANGES_UNINITIALIZED_H > diff --git a/libstdc++-v3/include/bits/stl_construct.h > b/libstdc++-v3/include/bits/stl_construct.h > index bd8235e901b..6d34edf02da 100644 > --- a/libstdc++-v3/include/bits/stl_construct.h > +++ b/libstdc++-v3/include/bits/stl_construct.h > @@ -144,6 +144,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #endif > > template<typename _T1> > +#if __cpp_constexpr >= 202406L // >= C++26 > + _GLIBCXX26_CONSTEXPR > +#endif Maybe we can get away with unconditionally declaring this _GLIBCXX26_CONSTEXPR? If the compiler doesn't support constexpr placement new then the 'constexpr' would be silently dropped at instantiation time. This would be in line with C++23 P2448R2 which made it no longer IFNDR to declare a constexpr function template for which no specialization is actually constexpr. > inline void > _Construct_novalue(_T1* __p) > { ::new(static_cast<void*>(__p)) _T1; } > diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h > b/libstdc++-v3/include/bits/stl_uninitialized.h > index ed836663a44..04f9792d37e 100644 > --- a/libstdc++-v3/include/bits/stl_uninitialized.h > +++ b/libstdc++-v3/include/bits/stl_uninitialized.h > @@ -68,6 +68,12 @@ > #include <bits/stl_iterator.h> // __niter_base > #include <ext/alloc_traits.h> // __alloc_traits > > +#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26 > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS constexpr > +#else > +# define _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > +#endif Similarly we could just use _GLIBCXX26_CONSTEXPR unconditionally throughout, instead of indirectly conditioning on __cpp_constexpr. > + > namespace std _GLIBCXX_VISIBILITY(default) > { > _GLIBCXX_BEGIN_NAMESPACE_VERSION > @@ -226,6 +232,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * Like std::copy, but does not require an initialized output range. > */ > template<typename _InputIterator, typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > uninitialized_copy(_InputIterator __first, _InputIterator __last, > _ForwardIterator __result) > @@ -256,6 +263,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > using _Src = decltype(std::__niter_base(__first)); > using _ValT = typename iterator_traits<_ForwardIterator>::value_type; > > + > + if (__is_constant_evaluated()) We typically call __is_constant_evaluated fully qualified (though I don't remember why since it's not eligible for ADL?) > + return std::__do_uninit_copy(__first, __last, __result); I guess we could instead guard the memcpy code path preceding the existing call to __do_uninit_copy with !std::__is_constant_evaluated(), rather than adding this new call. But that doesn't seem significantly cleaner and I'm personally OK with your approach :) Same for uninitialized_fill and uninitialized_fill_n > if constexpr (!__is_trivially_constructible(_ValT, decltype(*__first))) > return std::__do_uninit_copy(__first, __last, __result); > else if constexpr (__memcpyable<_Dest, _Src>::__value) > @@ -381,6 +391,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * Like std::fill, but does not require an initialized output range. > */ > template<typename _ForwardIterator, typename _Tp> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline void > uninitialized_fill(_ForwardIterator __first, _ForwardIterator __last, > const _Tp& __x) > @@ -400,6 +411,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > #if __cplusplus >= 201103L > #pragma GCC diagnostic push > #pragma GCC diagnostic ignored "-Wc++17-extensions" > + if (__is_constant_evaluated()) > + { > + std::__do_uninit_fill(__first, __last, __x); > + return; > + } > if constexpr (__is_byte<_ValueType>::__value) > if constexpr (is_same<_ValueType, _Tp>::value > || is_integral<_Tp>::value) > @@ -509,6 +525,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * Like std::fill_n, but does not require an initialized output range. > */ > template<typename _ForwardIterator, typename _Size, typename _Tp> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x) > { > @@ -522,6 +539,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > _ValueType; > > #if __cplusplus >= 201103L > + if (__is_constant_evaluated()) > + return std::__do_uninit_fill_n(__first, __n, __x); > if constexpr (__is_byte<_ValueType>::__value) > if constexpr (is_integral<_Tp>::value) > if constexpr (is_integral<_Size>::value) > @@ -815,6 +834,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct __uninitialized_default_1 > { > template<typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > static void > __uninit_default(_ForwardIterator __first, _ForwardIterator __last) > { > @@ -829,6 +849,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct __uninitialized_default_1<true> > { > template<typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > static void > __uninit_default(_ForwardIterator __first, _ForwardIterator __last) > { > @@ -882,6 +903,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > // __uninitialized_default > // Fills [first, last) with value-initialized value_types. > template<typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline void > __uninitialized_default(_ForwardIterator __first, > _ForwardIterator __last) > @@ -979,6 +1001,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct __uninitialized_default_novalue_1 > { > template<typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > static void > __uninit_default_novalue(_ForwardIterator __first, > _ForwardIterator __last) > @@ -994,6 +1017,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct __uninitialized_default_novalue_1<true> > { > template<typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > static void > __uninit_default_novalue(_ForwardIterator, _ForwardIterator) > { > @@ -1004,6 +1028,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct __uninitialized_default_novalue_n_1 > { > template<typename _ForwardIterator, typename _Size> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > static _ForwardIterator > __uninit_default_novalue_n(_ForwardIterator __first, _Size __n) > { > @@ -1019,6 +1044,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > struct __uninitialized_default_novalue_n_1<true> > { > template<typename _ForwardIterator, typename _Size> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > static _ForwardIterator > __uninit_default_novalue_n(_ForwardIterator __first, _Size __n) > { return std::next(__first, __n); } > @@ -1027,6 +1053,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > // __uninitialized_default_novalue > // Fills [first, last) with default-initialized value_types. > template<typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline void > __uninitialized_default_novalue(_ForwardIterator __first, > _ForwardIterator __last) > @@ -1042,6 +1069,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > // __uninitialized_default_novalue_n > // Fills [first, first + n) with default-initialized value_types. > template<typename _ForwardIterator, typename _Size> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > __uninitialized_default_novalue_n(_ForwardIterator __first, _Size __n) > { > @@ -1055,6 +1083,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<typename _InputIterator, typename _Size, > typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > _ForwardIterator > __uninitialized_copy_n(_InputIterator __first, _Size __n, > _ForwardIterator __result, input_iterator_tag) > @@ -1068,6 +1097,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<typename _RandomAccessIterator, typename _Size, > typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > __uninitialized_copy_n(_RandomAccessIterator __first, _Size __n, > _ForwardIterator __result, > @@ -1076,6 +1106,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<typename _InputIterator, typename _Size, > typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > pair<_InputIterator, _ForwardIterator> > __uninitialized_copy_n_pair(_InputIterator __first, _Size __n, > _ForwardIterator __result, input_iterator_tag) > @@ -1089,6 +1120,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > template<typename _RandomAccessIterator, typename _Size, > typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline pair<_RandomAccessIterator, _ForwardIterator> > __uninitialized_copy_n_pair(_RandomAccessIterator __first, _Size __n, > _ForwardIterator __result, > @@ -1112,6 +1144,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * Like copy_n(), but does not require an initialized output range. > */ > template<typename _InputIterator, typename _Size, typename > _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > uninitialized_copy_n(_InputIterator __first, _Size __n, > _ForwardIterator __result) > @@ -1120,6 +1153,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > /// @cond undocumented > template<typename _InputIterator, typename _Size, typename > _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline pair<_InputIterator, _ForwardIterator> > __uninitialized_copy_n_pair(_InputIterator __first, _Size __n, > _ForwardIterator __result) > @@ -1139,6 +1173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * @since C++17 > */ > template <typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline void > uninitialized_default_construct(_ForwardIterator __first, > _ForwardIterator __last) > @@ -1154,6 +1189,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * @since C++17 > */ > template <typename _ForwardIterator, typename _Size> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > uninitialized_default_construct_n(_ForwardIterator __first, _Size > __count) > { > @@ -1167,6 +1203,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * @since C++17 > */ > template <typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline void > uninitialized_value_construct(_ForwardIterator __first, > _ForwardIterator __last) > @@ -1182,6 +1219,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * @since C++17 > */ > template <typename _ForwardIterator, typename _Size> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > uninitialized_value_construct_n(_ForwardIterator __first, _Size __count) > { > @@ -1197,6 +1235,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * @since C++17 > */ > template <typename _InputIterator, typename _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline _ForwardIterator > uninitialized_move(_InputIterator __first, _InputIterator __last, > _ForwardIterator __result) > @@ -1215,6 +1254,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > * @since C++17 > */ > template <typename _InputIterator, typename _Size, typename > _ForwardIterator> > + _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > inline pair<_InputIterator, _ForwardIterator> > uninitialized_move_n(_InputIterator __first, _Size __count, > _ForwardIterator __result) > @@ -1324,4 +1364,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace > > +#undef _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS > + > #endif /* _STL_UNINITIALIZED_H */ > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 56843f817e8..7d10c0c4807 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -521,6 +521,11 @@ ftms = { > > ftms = { > name = raw_memory_algorithms; > + values = { > + v = 202411; > + cxxmin = 26; > + extra_cond = "__cpp_constexpr >= 202406L"; > + }; > values = { > v = 201606; > cxxmin = 17; > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 8db03dd33b2..e6a306c8ad8 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -581,7 +581,12 @@ > #undef __glibcxx_want_gcd_lcm > > #if !defined(__cpp_lib_raw_memory_algorithms) > -# if (__cplusplus >= 201703L) > +# if (__cplusplus > 202302L) && (__cpp_constexpr >= 202406L) > +# define __glibcxx_raw_memory_algorithms 202411L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_raw_memory_algorithms) > +# define __cpp_lib_raw_memory_algorithms 202411L > +# endif > +# elif (__cplusplus >= 201703L) > # define __glibcxx_raw_memory_algorithms 201606L > # if defined(__glibcxx_want_all) || > defined(__glibcxx_want_raw_memory_algorithms) > # define __cpp_lib_raw_memory_algorithms 201606L > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc > new file mode 100644 > index 00000000000..0252753aa66 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/feature_test_macro.cc > @@ -0,0 +1,14 @@ > +// { dg-do compile { target c++17 } } > +// { dg-add-options no_pch } > + > +#include <memory> > + > +#ifndef __cpp_lib_raw_memory_algorithms > +# error "Feature-test macro for raw memory algorithms missing" > +#elif __cplusplus > 202302L > +# if __cpp_lib_raw_memory_algorithms < 202411L > +# error "Feature-test macro for raw memory algorithms has wrong value" > +# endif > +#elif __cpp_lib_raw_memory_algorithms < 201606L > +# error "Feature-test macro for raw memory algorithms has wrong value" > +#endif > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc > > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc > new file mode 100644 > index 00000000000..6f05b0ce309 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_copy/constexpr.cc > @@ -0,0 +1,58 @@ > +// { dg-do compile { target c++26 } } > + > +#include <algorithm> > +#include <memory> > +#include <span> > +#include <string> > +#include <vector> > + > +template<typename T> > +constexpr > +bool > +test01_impl(std::vector<T> input) > +{ > + static_assert(std::copy_constructible<T>); > + static_assert(std::equality_comparable<T>); > + > + const std::size_t input_size = input.size(); > + std::allocator<T> alloc; > + T* ptr = alloc.allocate(input_size); > + > + std::uninitialized_copy(input.begin(), input.end(), ptr); > + if (!std::equal(input.begin(), input.end(), ptr, ptr + input_size)) > + return false; > + std::destroy(ptr, ptr + input_size); > + > + std::uninitialized_copy_n(input.begin(), input_size, ptr); > + if (!std::equal(input.begin(), input.end(), ptr, ptr + input_size)) > + return false; > + std::destroy_n(ptr, input_size); > + > + std::span<T> output(ptr, ptr + input_size); > + std::ranges::uninitialized_copy(input, output); > + if (!std::ranges::equal(input, output)) > + return false; > + std::ranges::destroy(output); > + > + std::ranges::uninitialized_copy_n(input.begin(), input_size, ptr, ptr + > input_size); > + if (!std::ranges::equal(input.begin(), input.end(), ptr, ptr + input_size)) > + return false; > + std::ranges::destroy_n(ptr, input_size); > + > + alloc.deallocate(ptr, input_size); > + return true; > +} > + > +constexpr > +bool > +test01() > +{ > + return > + test01_impl<char>({'a', 'b', 'c'}) && > + test01_impl<int>({1, 2, 3, 4}) && > + test01_impl<double>({1.0, 2.0, 3.0, 4.0}) && > + test01_impl<std::string>({"a", "b", "cc", "dddd", "eeeeeeeeeeeeeeee"}) && > + test01_impl<std::vector<int>>({ {0}, {0, 1}, {0, 1, 2}}); > +} > + > +static_assert(test01()); > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc > > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc > new file mode 100644 > index 00000000000..db39c8b4d05 > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/constexpr.cc > @@ -0,0 +1,67 @@ > +// { dg-do compile { target c++26 } } > + > +#include <algorithm> > +#include <memory> > +#include <span> > +#include <string> > +#include <vector> > + > +template<typename T> > +constexpr > +bool > +test01_impl() > +{ > + static_assert(std::default_initializable<T>); > + static_assert(std::equality_comparable<T>); > + > + constexpr std::size_t size = 42; > + std::allocator<T> alloc; > + T* ptr = alloc.allocate(size); > + > + auto check = [&]() -> bool > + { > + if constexpr (!std::is_trivially_default_constructible_v<T>) > + return std::all_of(ptr, ptr + size, [](auto &&x) { return x == T(); }); > + else > + return true; > + }; > + > + std::uninitialized_default_construct(ptr, ptr + size); > + if (!check()) > + return false; > + std::destroy(ptr, ptr + size); > + > + std::uninitialized_default_construct_n(ptr, size); > + if (!check()) > + return false; > + std::destroy_n(ptr, size); > + > + std::span<T> storage(ptr, ptr + size); > + std::ranges::uninitialized_default_construct(storage); > + if (!check()) > + return false; > + std::ranges::destroy(storage); > + > + std::ranges::uninitialized_default_construct_n(ptr, size); > + if (!check()) > + return false; > + std::ranges::destroy_n(ptr, size); > + > + alloc.deallocate(ptr, size); > + return true; > +} > + > +constexpr > +bool > +test01() > +{ > + return > + test01_impl<char>() && > + test01_impl<int>() && > + test01_impl<double>() && > + test01_impl<std::string>() && > + test01_impl<std::vector<int>>() && > + test01_impl<std::unique_ptr<int>>(); > +} > + > +static_assert(test01()); > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc > > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc > new file mode 100644 > index 00000000000..e43cd35a92d > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/constexpr.cc > @@ -0,0 +1,68 @@ > +// { dg-do compile { target c++26 } } > + > +#include <algorithm> > +#include <memory> > +#include <span> > +#include <string> > +#include <vector> > + > +template<typename T, typename U = T> > +constexpr > +bool > +test01_impl(const U& value = U()) > +{ > + static_assert(std::constructible_from<T, U>); > + //static_assert(std::equality_comparable_with<T, U>); // unique_ptr fails > with nullptr_t > + > + constexpr std::size_t size = 42; > + std::allocator<T> alloc; > + T* ptr = alloc.allocate(size); > + > + auto check = [&]() -> bool > + { > + return std::all_of(ptr, ptr + size, [&](auto &&x) { return x == value; > }); > + }; > + > + std::uninitialized_fill(ptr, ptr + size, value); > + if (!check()) > + return false; > + std::destroy(ptr, ptr + size); > + > + std::uninitialized_fill_n(ptr, size, value); > + if (!check()) > + return false; > + std::destroy_n(ptr, size); > + > + std::span<T> storage(ptr, ptr + size); > + std::ranges::uninitialized_fill(storage, value); > + if (!check()) > + return false; > + std::ranges::destroy(storage); > + > + std::ranges::uninitialized_fill_n(ptr, size, value); > + if (!check()) > + return false; > + std::ranges::destroy_n(ptr, size); > + > + alloc.deallocate(ptr, size); > + return true; > +} > + > +constexpr > +bool > +test01() > +{ > + return > + test01_impl<char>('\0') && > + test01_impl<char>('x') && > + test01_impl<int>(0) && > + test01_impl<int>(42) && > + test01_impl<double>(3.14) && > + test01_impl<std::string>() && > + test01_impl<std::string>(std::string("test")) && > + test01_impl<std::vector<int>>() && > + test01_impl<std::vector<int>>({1, 2, 3, 4}) && > + test01_impl<std::unique_ptr<int>>(nullptr); > +} > + > +static_assert(test01()); > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc > > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc > new file mode 100644 > index 00000000000..47403ae706d > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_move/constexpr.cc > @@ -0,0 +1,51 @@ > +// { dg-do compile { target c++26 } } > + > +#include <algorithm> > +#include <memory> > +#include <span> > +#include <string> > +#include <vector> > + > +template<typename T> > +constexpr > +bool > +test01_impl(std::vector<T> input) > +{ > + static_assert(std::move_constructible<T>); > + static_assert(std::equality_comparable<T>); > + > + const std::size_t input_size = input.size(); > + std::allocator<T> alloc; > + T* ptr = alloc.allocate(input_size); > + > + std::uninitialized_move(input.begin(), input.end(), ptr); > + std::destroy(ptr, ptr + input_size); > + > + std::uninitialized_move_n(input.begin(), input_size, ptr); > + std::destroy_n(ptr, input_size); > + > + std::span<T> output(ptr, ptr + input_size); > + std::ranges::uninitialized_move(input, output); > + std::ranges::destroy(output); > + > + std::ranges::uninitialized_move_n(input.begin(), input_size, ptr, ptr + > input_size); > + std::ranges::destroy_n(ptr, input_size); > + > + alloc.deallocate(ptr, input_size); > + return true; > +} > + > +constexpr > +bool > +test01() > +{ > + return > + test01_impl<char>({'a', 'b', 'c'}) && > + test01_impl<int>({1, 2, 3, 4}) && > + test01_impl<double>({1.0, 2.0, 3.0, 4.0}) && > + test01_impl<std::string>({"a", "b", "cc", "dddd", "eeeeeeeeeeeeeeee"}) && > + test01_impl<std::vector<int>>({ {0}, {0, 1}, {0, 1, 2}}) && > + test01_impl<std::unique_ptr<int>>(std::vector<std::unique_ptr<int>>(10)); > +} > + > +static_assert(test01()); > diff --git > a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc > > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc > new file mode 100644 > index 00000000000..55dfc59b5ef > --- /dev/null > +++ > b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/constexpr.cc > @@ -0,0 +1,64 @@ > +// { dg-do compile { target c++26 } } > + > +#include <algorithm> > +#include <memory> > +#include <span> > +#include <string> > +#include <vector> > + > +template<typename T> > +constexpr > +bool > +test01_impl() > +{ > + static_assert(std::default_initializable<T>); > + static_assert(std::equality_comparable<T>); > + > + constexpr std::size_t size = 42; > + std::allocator<T> alloc; > + T* ptr = alloc.allocate(size); > + > + auto check = [&]() -> bool > + { > + return std::all_of(ptr, ptr + size, [](auto &&x) { return x == T(); }); > + }; > + > + std::uninitialized_value_construct(ptr, ptr + size); > + if (!check()) > + return false; > + std::destroy(ptr, ptr + size); > + > + std::uninitialized_value_construct_n(ptr, size); > + if (!check()) > + return false; > + std::destroy_n(ptr, size); > + > + std::span<T> storage(ptr, ptr + size); > + std::ranges::uninitialized_value_construct(storage); > + if (!check()) > + return false; > + std::ranges::destroy(storage); > + > + std::ranges::uninitialized_value_construct_n(ptr, size); > + if (!check()) > + return false; > + std::ranges::destroy_n(ptr, size); > + > + alloc.deallocate(ptr, size); > + return true; > +} > + > +constexpr > +bool > +test01() > +{ > + return > + test01_impl<char>() && > + test01_impl<int>() && > + test01_impl<double>() && > + test01_impl<std::string>() && > + test01_impl<std::vector<int>>() && > + test01_impl<std::unique_ptr<int>>(); > +} > + > +static_assert(test01()); > -- > 2.34.1 >