Hello,

On 25/02/2025 23:46, Jonathan Wakely wrote:
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.

Yeah, for internal functions that aren't ever compiled as C++98, we
can often just make them constexpr. It will never be called during
constant evaluation in C++20 or older, but that's usually fine.

In this case though, would the placement new make it ill-formed in
Clang 18, which didn't support P2448R2?

Yes, I had the same question. Cppreference says that Clang 17/18 have a partial implementation of P2448, not sure what that means:

https://en.cppreference.com/w/cpp/23

A quick test shows that it seems happy with a placement new in a constexpr function in C++17 mode, but I'm not positive it won't complain in some other case.

So I'm going to leave these macros alone if you don't mind.


We typically call __is_constant_evaluated fully qualified (though I
don't remember why since it's not eligible for ADL?)

I don't think we're consistent, and it's not necessary.

But we could simplify things a little by doing:

#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
   if consteval {
     return std::__do_uninit_copy(__first, __last, __result);
   }
#endif

We don't need to use the __is_constant_evaluated() wrapper, or even
the std::is_constant_evaluated() function, because this is C++26 code
so we know 'if consteval' works. Clang supports it since version 14,
which is too old to support any C++26 mode, so every Clang that
supports -std=c++2c also supports 'if consteval'.

I'm not sure if the extra FTM check makes it more readable, but I guess `if consteval` is strictly better than the wrappers, so I'll amend this way.

New patch is attached.

Thank you,

--
Giuseppe D'Angelo
From f4257d81d4314bc415c283df48e2b5cbc95abc8c 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.

The only "real" change to the implementation of the algorithms is that
during constant evaluation I dispatch to a constexpr-friendly version of
them.

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 | 47 +++++++++++++
 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, 412 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..83837062d65 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,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using _Src = decltype(std::__niter_base(__first));
       using _ValT = typename iterator_traits<_ForwardIterator>::value_type;
 
+#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
+      if consteval {
+	return std::__do_uninit_copy(__first, __last, __result);
+      }
+#endif
       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 +393,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 +413,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #if __cplusplus >= 201103L
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wc++17-extensions"
+#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
+      if consteval {
+	return std::__do_uninit_fill(__first, __last, __x);
+      }
+#endif
       if constexpr (__is_byte<_ValueType>::__value)
 	if constexpr (is_same<_ValueType, _Tp>::value
 			|| is_integral<_Tp>::value)
@@ -509,6 +527,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 +541,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_ValueType;
 
 #if __cplusplus >= 201103L
+#if __glibcxx_raw_memory_algorithms >= 202411L // >= C++26
+      if consteval {
+	return std::__do_uninit_fill_n(__first, __n, __x);
+      }
+#endif
       if constexpr (__is_byte<_ValueType>::__value)
 	if constexpr (is_integral<_Tp>::value)
 	  if constexpr (is_integral<_Size>::value)
@@ -815,6 +839,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 +854,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 +908,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 +1006,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 +1022,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 +1033,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 +1049,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 +1058,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 +1074,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 +1088,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 +1102,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 +1111,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 +1125,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 +1149,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 +1158,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 +1178,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 +1194,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 +1208,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 +1224,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 +1240,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 +1259,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 +1369,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 9b1c236d817..1e979c79d20 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 92003b5365b..085309bbdfa 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