On Wed, Jul 16, 2025 at 12:56 PM Jakub Jelinek <ja...@redhat.com> wrote:
> On Mon, Jul 14, 2025 at 12:11:18PM +0200, Tomasz Kaminski wrote: > > We have a preference to use [[likely]] attribute when possible. > > Done. > > > > + for (; __first != __last; ++__first, ++__result) > > > + { > > > + if constexpr (is_array_v<_Tp>) > > > + std::relocate(std::begin(*__first), std::end(*__first), > > > + &(*__result)[0]); > > > > > We should use std::addressof or __builtin_addressof here to avoid using > > operator& found by ADL. The standard uses start_lifetime_as here ( > > https://eel.is/c++draft/memory#obj.lifetime-18.3.1), > > with the intent of constructing array and making pointer arithmetic on > > __res well-formed. > > Used std::addressof for now (until start_lifetime_as is added). > > > I was expecting this to be ill-formed at compile time, as we never > > start the lifetime of array S[6] inside the a, so I believed things like > > pointer > > arithmetic on sf would be ill-formed. In other words I believe that this > > test > > would require invoking start_lifetime_as. > > > > But continuing on that, is there any reason to use unsigned char array > > for this test, instead of having arrays instead of unamed union as source > > and destination: > > union { S a[6]; }; > > union { S b[6]; }; > > This will still allow manual memory management, but will make code more > > readable, at least for me. > > > > Jonathan, do you have preference here? > > Here is an updated patch, which has 2 functions, test_relocate > is as before but without constexpr and > - tr = ::new(&a[1]) T(42); > + tr = ::new(&b[1]) T(42); > fix and then test_relocate_constexpr which uses array of S and > just destructs all members of the array before doing placement new > again into it, std::relocate etc. And finally at the end of function > it constructs there S objects in all elements again. > > I have no idea how to test the array cases at constant evaluation > time, placement new doesn't return pointer to array and I get > complains about reinterpret_cast not being valid during constant evaluation > time. > Array relocation at compile time is not supported per standard wording, because it is defined to call start_lifetime_as, which is not constexpr. > So, this patch just tests non-array std::relocate at both runtime and > constexpr time, everything else at runtime only. > > 2025-07-16 Jakub Jelinek <ja...@redhat.com> > > PR c++/119064 > * include/bits/version.def (trivially_relocatable): New. > * include/bits/version.h: Regenerate. > * include/std/type_traits (std::is_trivially_relocatable, > std::is_nothrow_relocatable, std::is_replaceable): New traits. > std::is_trivially_relocatable_v, std::is_nothrow_relocatable_v, > std::is_replaceable_v): New trait variable templates. > * include/std/memory (__glibcxx_want_trivially_relocatable): Define > before including bits/version.h. > (std::trivially_relocate): New template function. > (std::relocate): Likewise. > * testsuite/std/memory/relocate/relocate.cc: New test. > > --- libstdc++-v3/include/bits/version.def.jj 2025-07-16 > 07:06:05.645224306 +0200 > +++ libstdc++-v3/include/bits/version.def 2025-07-16 > 11:51:19.121688727 +0200 > @@ -2059,6 +2059,15 @@ ftms = { > }; > }; > > +ftms = { > + name = trivially_relocatable; > + values = { > + v = 202502; > + cxxmin = 26; > + extra_cond = "__cpp_trivial_relocatability >= 202502L"; > + }; > +}; > + > // Standard test specifications. > stds[97] = ">= 199711L"; > stds[03] = ">= 199711L"; > --- libstdc++-v3/include/bits/version.h.jj 2025-07-16 > 07:06:14.934235823 +0200 > +++ libstdc++-v3/include/bits/version.h 2025-07-16 11:51:19.122688713 +0200 > @@ -2309,4 +2309,14 @@ > #endif /* !defined(__cpp_lib_constexpr_exceptions) && > defined(__glibcxx_want_constexpr_exceptions) */ > #undef __glibcxx_want_constexpr_exceptions > > +#if !defined(__cpp_lib_trivially_relocatable) > +# if (__cplusplus > 202302L) && (__cpp_trivial_relocatability >= 202502L) > +# define __glibcxx_trivially_relocatable 202502L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_trivially_relocatable) > +# define __cpp_lib_trivially_relocatable 202502L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_trivially_relocatable) && > defined(__glibcxx_want_trivially_relocatable) */ > +#undef __glibcxx_want_trivially_relocatable > + > #undef __glibcxx_want_all > --- libstdc++-v3/include/std/type_traits.jj 2025-07-15 > 14:49:29.976515413 +0200 > +++ libstdc++-v3/include/std/type_traits 2025-07-16 > 11:51:19.122688713 +0200 > @@ -4280,6 +4280,60 @@ template<typename _Ret, typename _Fn, ty > > #endif // C++2a > > +#if __glibcxx_trivially_relocatable >= 202502L // C++ >= 26 && > __cpp_trivial_relocatability >= 202502 > + /// True if the type is a trivially relocatable type. > + /// @since C++26 > + > + template<typename _Tp> > + struct is_trivially_relocatable > +# if __has_builtin(__builtin_is_trivially_relocatable) > + : bool_constant<__builtin_is_trivially_relocatable(_Tp)> > +# else > + : bool_constant<__builtin_is_cpp_trivially_relocatable(_Tp)> > +# endif > + { }; > + > + template<typename _Tp> > + struct is_nothrow_relocatable > +# if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_nothrow_relocatable) > + : bool_constant<__builtin_is_nothrow_relocatable(_Tp)> > +# else > + : public __or_<is_trivially_relocatable<_Tp>, > + > __and_<is_nothrow_move_constructible<remove_all_extents<_Tp>>, > + > is_nothrow_destructible<remove_all_extends<_Tp>>>>::type > +# endif > + { }; > + > + template<typename _Tp> > + struct is_replaceable > + : bool_constant<__builtin_is_replaceable(_Tp)> > + { }; > + > + /// @ingroup variable_templates > + /// @since C++26 > + template<typename _Tp> > + inline constexpr bool is_trivially_relocatable_v > +# if __has_builtin(__builtin_is_trivially_relocatable) > + = __builtin_is_trivially_relocatable(_Tp); > +# else > + = __builtin_is_cpp_trivially_relocatable(_Tp); > +# endif > + > + template<typename _Tp> > + inline constexpr bool is_nothrow_relocatable_v > +# if _GLIBCXX_USE_BUILTIN_TRAIT(__builtin_is_nothrow_relocatable) > + = __builtin_is_nothrow_relocatable(_Tp); > +# else > + = (is_trivially_relocatable_v<_Tp> > + || (is_nothrow_move_constructible_v<remove_all_extents<_Tp>> > + && is_nothrow_destructible_v<remove_all_extents<_Tp>>); > +# endif > + > + template<typename _Tp> > + inline constexpr bool is_replaceable_v > + = __builtin_is_replaceable(_Tp); > +#endif > + > /// @} group metaprogramming > > _GLIBCXX_END_NAMESPACE_VERSION > --- libstdc++-v3/include/std/memory.jj 2025-07-09 20:38:59.050627934 +0200 > +++ libstdc++-v3/include/std/memory 2025-07-16 11:51:50.888265934 +0200 > @@ -122,6 +122,7 @@ > #define __glibcxx_want_to_address > #define __glibcxx_want_transparent_operators > #define __glibcxx_want_smart_ptr_owner_equality > +#define __glibcxx_want_trivially_relocatable > #include <bits/version.h> > > #if __cplusplus >= 201103L && __cplusplus <= 202002L && _GLIBCXX_HOSTED > @@ -171,6 +172,121 @@ _GLIBCXX_END_NAMESPACE_VERSION > } // namespace > #endif // C++11 to C++20 > > +#ifdef __cpp_lib_trivially_relocatable > +namespace std _GLIBCXX_VISIBILITY(default) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > + > + template<typename _Tp> > + [[__gnu__::__always_inline__]] > + inline _Tp* > + trivially_relocate(_Tp* __first, _Tp* __last, _Tp* __result) noexcept > + { > + static_assert(is_trivially_relocatable_v<_Tp> && !is_const_v<_Tp>); > + if (__first == __result) > + return __last; > +#if __has_builtin(__builtin_trivially_relocate) > + __builtin_trivially_relocate(__result, __first, __last - __first); > +#else > + __builtin_memmove(static_cast<void*>(__result), > + static_cast<const void*>(__first), > + (__last - __first) * sizeof(_Tp)); > +#endif > + return __result + (__last - __first); > + } > + > + template<typename _Tp> > + constexpr _Tp* > + relocate(_Tp* __first, _Tp* __last, _Tp* __result) noexcept > + { > + static_assert(is_nothrow_relocatable_v<_Tp> && !is_const_v<_Tp>); > + if !consteval > + { > + if constexpr (is_trivially_relocatable_v<_Tp>) > + return std::trivially_relocate(__first, __last, __result); > + } > + if (__first == __result) > + return __last; > + if (__first == __last) > + return __result; > + > + size_t __n = __last - __first; > + if constexpr (is_move_constructible_v<_Tp>) > + { > + if !consteval > + { > + // If there is no overlap, move everything first and then > + // destruct. > + if ((__UINTPTR_TYPE__)__last <= (__UINTPTR_TYPE__)__result > + || ((__UINTPTR_TYPE__)(__result + __n) > + <= (__UINTPTR_TYPE__)__first)) > + { > + __result = std::uninitialized_move(__first, __last, > + __result); > + std::destroy(__first, __last); > + return __result; > + } > + } > + } > + > + bool __fwd = true; > + if consteval > + { > + // Use __builtin_constant_p to determine if __result and __first > + // point into the same object, if they don't, there is no overlap > + // and so either __fwd or !__fwd is fine. > + if (__builtin_constant_p (__result < __first) > + && __result > __first > + && __result < __last) > + __fwd = false; > + } > + else > + { > + __fwd = ((__UINTPTR_TYPE__)__result - (__UINTPTR_TYPE__)__first > + >= (__UINTPTR_TYPE__)__last - > (__UINTPTR_TYPE__)__first); > + } > + if (__fwd) [[likely]] > + { > + for (; __first != __last; ++__first, ++__result) > + { > + if constexpr (is_array_v<_Tp>) > + std::relocate(std::begin(*__first), std::end(*__first), > + std::addressof(*__result)[0]); > + else > + { > + ::new(__result) _Tp(std::move(*__first)); > + __first->~_Tp(); > + } > + } > + return __result; > + } > + else > + { > + _Tp *__ret = __result + __n; > + for (__result = __ret; __last != __first; ) > + { > + --__last; > + --__result; > + if constexpr (is_array_v<_Tp>) > + std::relocate(std::begin(*__last), std::end(*__last), > + std::addressof(*__result)[0]); > + else > + { > + ::new(__result) _Tp(std::move(*__last)); > + __last->~_Tp(); > + } > + } > + return __ret; > + } > + // If at constand evaluation is_trivially_relocatable_v<_Tp> > + // but not is_move_constructible_V<_Tp>, fail. > + __builtin_unreachable(); > + } > + > +_GLIBCXX_END_NAMESPACE_VERSION > +} // namespace > +#endif > + > #ifdef __cpp_lib_parallel_algorithm // C++ >= 17 && HOSTED > // Parallel STL algorithms > # if _PSTL_EXECUTION_POLICIES_DEFINED > --- libstdc++-v3/testsuite/std/memory/relocate/relocate.cc.jj 2025-07-16 > 11:51:19.124688687 +0200 > +++ libstdc++-v3/testsuite/std/memory/relocate/relocate.cc 2025-07-16 > 12:45:01.656814019 +0200 > @@ -0,0 +1,398 @@ > +// { dg-do run { target c++26 } } > + > +#include <memory> > + > +#include <testsuite_hooks.h> > +#include <testsuite_allocator.h> > + > +struct S trivially_relocatable_if_eligible > +{ > + constexpr S() : s(0) {} > + constexpr S(int x) : s(x) {} > + constexpr S(S&& x) : s(x.s) {} > + constexpr S& operator=(S&& x) { s = x.s; return *this; } > + unsigned char s; > +}; > + > +struct T > +{ > + constexpr T() : t(0) {} > + constexpr T(int x) : t(x) {} > + constexpr T(T&& x) noexcept : t(x.t) {} > + constexpr T& operator=(T&& x) { t = x.t; return *this; } > + unsigned char t; > +}; > + > +static_assert(std::is_trivially_relocatable_v<S>); > +static_assert(std::is_nothrow_relocatable_v<S>); > +static_assert(std::is_nothrow_relocatable_v<T>); > + > +void > +test_relocate() > +{ > + unsigned char a[20], b[20], c[20], d[20]; > + S *sf, *sl, *sr; > + T *tf, *tl, *tr; > + sf = ::new(&a[2]) S(11); > + tf = ::new(&b[2]) T(11); > + for (int i = 3; i < 8; ++i) > + { > + sl = ::new(&a[i]) S(i + 9); > + tl = ::new(&b[i]) T(i + 9); > + } > + ++sl; > + ++tl; > + sr = ::new(&c[5]) S(42); > + tr = ::new(&d[5]) T(42); > + sr->~S(); > + tr->~T(); > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + VERIFY( tr->t == i + 11 ); > + sr->~S(); > + tr->~T(); > + ++sr; > + ++tr; > + } > + sf = ::new(&a[2]) S(11); > + tf = ::new(&b[2]) T(11); > + for (int i = 3; i < 8; ++i) > + { > + sl = ::new(&a[i]) S(i + 9); > + tl = ::new(&b[i]) T(i + 9); > + } > + ++sl; > + ++tl; > + sr = ::new(&a[1]) S(42); > + tr = ::new(&b[1]) T(42); > + sr->~S(); > + tr->~T(); > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + VERIFY( tr->t == i + 11 ); > + sr->~S(); > + tr->~T(); > + ++sr; > + ++tr; > + } > + sf = ::new(&a[2]) S(11); > + tf = ::new(&b[2]) T(11); > + for (int i = 3; i < 8; ++i) > + { > + sl = ::new(&a[i]) S(i + 9); > + tl = ::new(&b[i]) T(i + 9); > + } > + ++sl; > + ++tl; > + sr = sf + 1; > + tr = tf + 1; > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + VERIFY( tr->t == i + 11 ); > + sr->~S(); > + tr->~T(); > + ++sr; > + ++tr; > + } > +} > + > +constexpr void > +test_relocate_constexpr() > +{ > + S a[20], c[20]; > + T b[20], d[20]; > + for (int i = 0; i < 20; ++i) > + { > + a[i].~S(); > + b[i].~T(); > + c[i].~S(); > + d[i].~T(); > + } > + S *sf, *sl, *sr; > + T *tf, *tl, *tr; > + sf = ::new(&a[2]) S(11); > + tf = ::new(&b[2]) T(11); > + for (int i = 3; i < 8; ++i) > + { > + sl = ::new(&a[i]) S(i + 9); > + tl = ::new(&b[i]) T(i + 9); > + } > + ++sl; > + ++tl; > + sr = ::new(&c[5]) S(42); > + tr = ::new(&d[5]) T(42); > + sr->~S(); > + tr->~T(); > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + VERIFY( tr->t == i + 11 ); > + sr->~S(); > + tr->~T(); > + ++sr; > + ++tr; > + } > + sf = ::new(&a[2]) S(11); > + tf = ::new(&b[2]) T(11); > + for (int i = 3; i < 8; ++i) > + { > + sl = ::new(&a[i]) S(i + 9); > + tl = ::new(&b[i]) T(i + 9); > + } > + ++sl; > + ++tl; > + sr = ::new(&a[1]) S(42); > + tr = ::new(&b[1]) T(42); > + sr->~S(); > + tr->~T(); > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + VERIFY( tr->t == i + 11 ); > + sr->~S(); > + tr->~T(); > + ++sr; > + ++tr; > + } > + sf = ::new(&a[2]) S(11); > + tf = ::new(&b[2]) T(11); > + for (int i = 3; i < 8; ++i) > + { > + sl = ::new(&a[i]) S(i + 9); > + tl = ::new(&b[i]) T(i + 9); > + } > + ++sl; > + ++tl; > + sr = sf + 1; > + tr = tf + 1; > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + VERIFY( tr->t == i + 11 ); > + sr->~S(); > + tr->~T(); > + ++sr; > + ++tr; > + } > + for (int i = 0; i < 20; ++i) > + { > + ::new(&a[i]) S(0); > + ::new(&b[i]) T(0); > + ::new(&c[i]) S(0); > + ::new(&d[i]) T(0); > + } > +} > + > +void > +test_relocate_array() > +{ > + unsigned char a[20], b[20], c[20], d[20]; > + using SA = S[2]; > + using TA = T[2]; > + SA *sf, *sl, *sr; > + TA *tf, *tl, *tr; > + sf = (SA *)(::new(&a[2]) SA{11, 12}); > + tf = (TA *)(::new(&b[2]) TA{11, 12}); > + for (int i = 0; i < 5; ++i) > + { > + sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i}); > + tl = (TA *)(::new(&b[4 + 2 * i]) TA{13 + 2 * i, 14 + 2 * i}); > + } > + ++sl; > + ++tl; > + sr = (SA *)(::new(&c[6]) SA{42, 42}); > + tr = (TA *)(::new(&d[6]) TA{42, 42}); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + (*tr)[0].~T(); > + (*tr)[1].~T(); > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 ); > + VERIFY( (*tr)[0].t == 2 * i + 11 && (*tr)[1].t == 2 * i + 12 ); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + (*tr)[0].~T(); > + (*tr)[1].~T(); > + ++sr; > + ++tr; > + } > + sf = (SA *)(::new(&a[2]) SA{11, 12}); > + tf = (TA *)(::new(&b[2]) TA{11, 12}); > + for (int i = 0; i < 5; ++i) > + { > + sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i}); > + tl = (TA *)(::new(&b[4 + 2 * i]) TA{13 + 2 * i, 14 + 2 * i}); > + } > + ++sl; > + ++tl; > + sr = (SA *)(::new(&a[0]) SA{42, 42}); > + tr = (TA *)(::new(&b[0]) TA{42, 42}); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + (*tr)[0].~T(); > + (*tr)[1].~T(); > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 ); > + VERIFY( (*tr)[0].t == 2 * i + 11 && (*tr)[1].t == 2 * i + 12 ); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + (*tr)[0].~T(); > + (*tr)[1].~T(); > + ++sr; > + ++tr; > + } > + sf = (SA *)(::new(&a[2]) SA{11, 12}); > + tf = (TA *)(::new(&b[2]) TA{11, 12}); > + for (int i = 0; i < 5; ++i) > + { > + sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i}); > + tl = (TA *)(::new(&b[4 + 2 * i]) TA{13 + 2 * i, 14 + 2 * i}); > + } > + ++sl; > + ++tl; > + sr = sf + 1; > + tr = tf + 1; > + VERIFY( std::relocate(sf, sl, sr) == sr + 6 ); > + VERIFY( std::relocate(tf, tl, tr) == tr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 ); > + VERIFY( (*tr)[0].t == 2 * i + 11 && (*tr)[1].t == 2 * i + 12 ); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + (*tr)[0].~T(); > + (*tr)[1].~T(); > + ++sr; > + ++tr; > + } > +} > + > +void > +test_trivially_relocate() > +{ > + unsigned char a[20], c[20]; > + S *sf, *sl, *sr; > + sf = ::new(&a[2]) S(11); > + for (int i = 3; i < 8; ++i) > + sl = ::new(&a[i]) S(i + 9); > + ++sl; > + sr = ::new(&c[5]) S(42); > + sr->~S(); > + VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + sr->~S(); > + ++sr; > + } > + sf = ::new(&a[2]) S(11); > + for (int i = 3; i < 8; ++i) > + sl = ::new(&a[i]) S(i + 9); > + ++sl; > + sr = ::new(&a[1]) S(42); > + sr->~S(); > + VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + sr->~S(); > + ++sr; > + } > + sf = ::new(&a[2]) S(11); > + for (int i = 3; i < 8; ++i) > + sl = ::new(&a[i]) S(i + 9); > + ++sl; > + sr = sf + 1; > + VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( sr->s == i + 11 ); > + sr->~S(); > + ++sr; > + } > +} > + > +void > +test_trivially_relocate_array() > +{ > + unsigned char a[20], c[20]; > + using SA = S[2]; > + SA *sf, *sl, *sr; > + sf = (SA *)(::new(&a[2]) SA{11, 12}); > + for (int i = 0; i < 5; ++i) > + sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i}); > + ++sl; > + sr = (SA *)(::new(&c[6]) SA{42, 42}); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 ); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + ++sr; > + } > + sf = (SA *)(::new(&a[2]) SA{11, 12}); > + for (int i = 0; i < 5; ++i) > + sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i}); > + ++sl; > + sr = (SA *)(::new(&a[0]) SA{42, 42}); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 ); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + ++sr; > + } > + sf = (SA *)(::new(&a[2]) SA{11, 12}); > + for (int i = 0; i < 5; ++i) > + sl = (SA *)(::new(&a[4 + 2 * i]) SA{13 + 2 * i, 14 + 2 * i}); > + ++sl; > + sr = sf + 1; > + VERIFY( std::trivially_relocate(sf, sl, sr) == sr + 6 ); > + for (int i = 0; i < 6; ++i) > + { > + VERIFY( (*sr)[0].s == 2 * i + 11 && (*sr)[1].s == 2 * i + 12 ); > + (*sr)[0].~S(); > + (*sr)[1].~S(); > + ++sr; > + } > +} > + > +int main() > +{ > + test_relocate(); > + test_relocate_constexpr(); > + test_trivially_relocate(); > + test_relocate_array(); > + test_trivially_relocate_array(); > + static_assert([] { > + test_relocate_constexpr(); > + return true; > + }()); > +} > > Jakub > >