[PATCH] libstdc++: implement constexpr memory algorithms
Patrick Palka
ppalka@redhat.com
Thu Feb 20 16:22:07 GMT 2025
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.dangelo@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
>
More information about the Libstdc++
mailing list