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 when compiling in C++26 and above modes. The internal helper
guard class for range algorithms instead can be marked unconditionally.

uninitialized_default_construct is implemented in terms of the
_Construct_novalue helper, which requires support for C++26's constexpr
placement new from the compiler (P2747R2, which GCC implements). We can
simply mark it as constexpr in C++26 language modes, even if the
compiler does not support P2747R2 (e.g. Clang 17/18), because C++23's
P2448R2 makes it OK to mark functions as constexpr even if they never
qualify, and other compilers implement this.

The only "real" change to the implementation of the algorithms is that
during constant evaluation I need to 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: (_Construct_novalue) Mark it
	as constexpr in C++26.
	* include/bits/version.def (raw_memory_algorithms): Bump the
	feature-testing macro for C++26.
	* include/bits/version.h: Regenerate.
	* testsuite/20_util/headers/memory/synopsis.cc: Add constexpr to
	the uninitialized_* algorithms (when in C++26) in the test.
	* 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.

Reviewed-by: Patrick Palka <ppalka@redhat.com>
This commit is contained in:
Giuseppe D'Angelo 2025-02-16 19:37:07 +01:00
parent 705ae582d5
commit 3052b33645
12 changed files with 406 additions and 1 deletions

View file

@ -105,15 +105,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 +129,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 +146,7 @@ namespace ranges
template<__detail::__nothrow_forward_iterator _Iter,
__detail::__nothrow_sentinel<_Iter> _Sent>
requires default_initializable<iter_value_t<_Iter>>
_GLIBCXX26_CONSTEXPR
_Iter
operator()(_Iter __first, _Sent __last) const
{
@ -159,6 +165,7 @@ namespace ranges
template<__detail::__nothrow_forward_range _Range>
requires default_initializable<range_value_t<_Range>>
_GLIBCXX26_CONSTEXPR
borrowed_iterator_t<_Range>
operator()(_Range&& __r) const
{
@ -173,6 +180,7 @@ namespace ranges
{
template<__detail::__nothrow_forward_iterator _Iter>
requires default_initializable<iter_value_t<_Iter>>
_GLIBCXX26_CONSTEXPR
_Iter
operator()(_Iter __first, iter_difference_t<_Iter> __n) const
{
@ -198,6 +206,7 @@ namespace ranges
template<__detail::__nothrow_forward_iterator _Iter,
__detail::__nothrow_sentinel<_Iter> _Sent>
requires default_initializable<iter_value_t<_Iter>>
_GLIBCXX26_CONSTEXPR
_Iter
operator()(_Iter __first, _Sent __last) const
{
@ -217,6 +226,7 @@ namespace ranges
template<__detail::__nothrow_forward_range _Range>
requires default_initializable<range_value_t<_Range>>
_GLIBCXX26_CONSTEXPR
borrowed_iterator_t<_Range>
operator()(_Range&& __r) const
{
@ -231,6 +241,7 @@ namespace ranges
{
template<__detail::__nothrow_forward_iterator _Iter>
requires default_initializable<iter_value_t<_Iter>>
_GLIBCXX26_CONSTEXPR
_Iter
operator()(_Iter __first, iter_difference_t<_Iter> __n) const
{
@ -261,6 +272,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
uninitialized_copy_result<_Iter, _Out>
operator()(_Iter __ifirst, _ISent __ilast,
_Out __ofirst, _OSent __olast) const
@ -292,6 +304,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
uninitialized_copy_result<borrowed_iterator_t<_IRange>,
borrowed_iterator_t<_ORange>>
operator()(_IRange&& __inr, _ORange&& __outr) const
@ -311,6 +324,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
uninitialized_copy_n_result<_Iter, _Out>
operator()(_Iter __ifirst, iter_difference_t<_Iter> __n,
_Out __ofirst, _Sent __olast) const
@ -350,6 +364,7 @@ namespace ranges
__detail::__nothrow_sentinel<_Out> _OSent>
requires constructible_from<iter_value_t<_Out>,
iter_rvalue_reference_t<_Iter>>
_GLIBCXX26_CONSTEXPR
uninitialized_move_result<_Iter, _Out>
operator()(_Iter __ifirst, _ISent __ilast,
_Out __ofirst, _OSent __olast) const
@ -384,6 +399,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
uninitialized_move_result<borrowed_iterator_t<_IRange>,
borrowed_iterator_t<_ORange>>
operator()(_IRange&& __inr, _ORange&& __outr) const
@ -404,6 +420,7 @@ namespace ranges
__detail::__nothrow_sentinel<_Out> _Sent>
requires constructible_from<iter_value_t<_Out>,
iter_rvalue_reference_t<_Iter>>
_GLIBCXX26_CONSTEXPR
uninitialized_move_n_result<_Iter, _Out>
operator()(_Iter __ifirst, iter_difference_t<_Iter> __n,
_Out __ofirst, _Sent __olast) const
@ -441,6 +458,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
_Iter
operator()(_Iter __first, _Sent __last, const _Tp& __x) const
{
@ -460,6 +478,7 @@ namespace ranges
template<__detail::__nothrow_forward_range _Range, typename _Tp>
requires constructible_from<range_value_t<_Range>, const _Tp&>
_GLIBCXX26_CONSTEXPR
borrowed_iterator_t<_Range>
operator()(_Range&& __r, const _Tp& __x) const
{
@ -473,6 +492,7 @@ namespace ranges
{
template<__detail::__nothrow_forward_iterator _Iter, typename _Tp>
requires constructible_from<iter_value_t<_Iter>, const _Tp&>
_GLIBCXX26_CONSTEXPR
_Iter
operator()(_Iter __first, iter_difference_t<_Iter> __n,
const _Tp& __x) const
@ -573,6 +593,7 @@ namespace ranges
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // concepts
#endif // C++20
#endif // _RANGES_UNINITIALIZED_H

View file

@ -144,6 +144,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif
template<typename _T1>
_GLIBCXX26_CONSTEXPR
inline void
_Construct_novalue(_T1* __p)
{ ::new(static_cast<void*>(__p)) _T1; }

View file

@ -226,6 +226,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* Like std::copy, but does not require an initialized output range.
*/
template<typename _InputIterator, typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline _ForwardIterator
uninitialized_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
@ -256,6 +257,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 +387,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* Like std::fill, but does not require an initialized output range.
*/
template<typename _ForwardIterator, typename _Tp>
_GLIBCXX26_CONSTEXPR
inline void
uninitialized_fill(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __x)
@ -400,6 +407,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 +521,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
inline _ForwardIterator
uninitialized_fill_n(_ForwardIterator __first, _Size __n, const _Tp& __x)
{
@ -522,6 +535,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 +833,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __uninitialized_default_1
{
template<typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
static void
__uninit_default(_ForwardIterator __first, _ForwardIterator __last)
{
@ -829,6 +848,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __uninitialized_default_1<true>
{
template<typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
static void
__uninit_default(_ForwardIterator __first, _ForwardIterator __last)
{
@ -882,6 +902,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// __uninitialized_default
// Fills [first, last) with value-initialized value_types.
template<typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline void
__uninitialized_default(_ForwardIterator __first,
_ForwardIterator __last)
@ -979,6 +1000,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __uninitialized_default_novalue_1
{
template<typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
static void
__uninit_default_novalue(_ForwardIterator __first,
_ForwardIterator __last)
@ -994,6 +1016,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __uninitialized_default_novalue_1<true>
{
template<typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
static void
__uninit_default_novalue(_ForwardIterator, _ForwardIterator)
{
@ -1004,6 +1027,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __uninitialized_default_novalue_n_1
{
template<typename _ForwardIterator, typename _Size>
_GLIBCXX26_CONSTEXPR
static _ForwardIterator
__uninit_default_novalue_n(_ForwardIterator __first, _Size __n)
{
@ -1019,6 +1043,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __uninitialized_default_novalue_n_1<true>
{
template<typename _ForwardIterator, typename _Size>
_GLIBCXX26_CONSTEXPR
static _ForwardIterator
__uninit_default_novalue_n(_ForwardIterator __first, _Size __n)
{ return std::next(__first, __n); }
@ -1027,6 +1052,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// __uninitialized_default_novalue
// Fills [first, last) with default-initialized value_types.
template<typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline void
__uninitialized_default_novalue(_ForwardIterator __first,
_ForwardIterator __last)
@ -1042,6 +1068,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
inline _ForwardIterator
__uninitialized_default_novalue_n(_ForwardIterator __first, _Size __n)
{
@ -1055,6 +1082,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Size,
typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
_ForwardIterator
__uninitialized_copy_n(_InputIterator __first, _Size __n,
_ForwardIterator __result, input_iterator_tag)
@ -1068,6 +1096,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _RandomAccessIterator, typename _Size,
typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline _ForwardIterator
__uninitialized_copy_n(_RandomAccessIterator __first, _Size __n,
_ForwardIterator __result,
@ -1076,6 +1105,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Size,
typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
pair<_InputIterator, _ForwardIterator>
__uninitialized_copy_n_pair(_InputIterator __first, _Size __n,
_ForwardIterator __result, input_iterator_tag)
@ -1089,6 +1119,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _RandomAccessIterator, typename _Size,
typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline pair<_RandomAccessIterator, _ForwardIterator>
__uninitialized_copy_n_pair(_RandomAccessIterator __first, _Size __n,
_ForwardIterator __result,
@ -1112,6 +1143,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
inline _ForwardIterator
uninitialized_copy_n(_InputIterator __first, _Size __n,
_ForwardIterator __result)
@ -1120,6 +1152,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// @cond undocumented
template<typename _InputIterator, typename _Size, typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline pair<_InputIterator, _ForwardIterator>
__uninitialized_copy_n_pair(_InputIterator __first, _Size __n,
_ForwardIterator __result)
@ -1139,6 +1172,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++17
*/
template <typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline void
uninitialized_default_construct(_ForwardIterator __first,
_ForwardIterator __last)
@ -1154,6 +1188,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++17
*/
template <typename _ForwardIterator, typename _Size>
_GLIBCXX26_CONSTEXPR
inline _ForwardIterator
uninitialized_default_construct_n(_ForwardIterator __first, _Size __count)
{
@ -1167,6 +1202,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++17
*/
template <typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline void
uninitialized_value_construct(_ForwardIterator __first,
_ForwardIterator __last)
@ -1182,6 +1218,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++17
*/
template <typename _ForwardIterator, typename _Size>
_GLIBCXX26_CONSTEXPR
inline _ForwardIterator
uninitialized_value_construct_n(_ForwardIterator __first, _Size __count)
{
@ -1197,6 +1234,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++17
*/
template <typename _InputIterator, typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline _ForwardIterator
uninitialized_move(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
@ -1215,6 +1253,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @since C++17
*/
template <typename _InputIterator, typename _Size, typename _ForwardIterator>
_GLIBCXX26_CONSTEXPR
inline pair<_InputIterator, _ForwardIterator>
uninitialized_move_n(_InputIterator __first, _Size __count,
_ForwardIterator __result)

View file

@ -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;

View file

@ -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

View file

@ -73,18 +73,30 @@ namespace std
template <class T> T* addressof(T&) noexcept;
#endif
template <class InputIterator, class ForwardIterator>
#if __cplusplus >= 202400L
constexpr
#endif
ForwardIterator
uninitialized_copy(InputIterator first, InputIterator last,
ForwardIterator result);
#if __cplusplus >= 201103L
template <class InputIterator, class Size, class ForwardIterator>
#if __cplusplus >= 202400L
constexpr
#endif
ForwardIterator
uninitialized_copy_n(InputIterator first, Size n, ForwardIterator result);
#endif
template <class ForwardIterator, class T>
#if __cplusplus >= 202400L
constexpr
#endif
void uninitialized_fill(ForwardIterator first, ForwardIterator last,
const T& x);
template <class ForwardIterator, class Size, class T>
#if __cplusplus >= 202400L
constexpr
#endif
void uninitialized_fill_n(ForwardIterator first, Size n, const T& x);
#if __cplusplus >= 201103L

View file

@ -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

View file

@ -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());

View file

@ -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());

View file

@ -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());

View file

@ -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());

View file

@ -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());