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
> 

Reply via email to