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
From 8a4224674bf267c80924a9f69c634f050c3ad590 Mon Sep 17 00:00:00 2001
From: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Date: Sun, 16 Feb 2025 19:37:07 +0100
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).

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
     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
+
 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())
+	return std::__do_uninit_copy(__first, __last, __result);
       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

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to