Date: Thu, 30 Oct 2025 00:05:47 +0000
On Wed, Oct 29, 2025 at 9:51 PM Frederick Virchanza Gotham wrote:
>
>
> How about if 'is_trivially_relocatable_v' were to be taken out and replaced with 'relocatability_v':
>
> namespace std {
> template<typename T>
> struct relocatability : integral_constant<int, . . . >{};
> }
Implementation might look something like this:
https://godbolt.org/z/M57xh1TvY
And here it is copy-pasted:
#include <cstddef>
#include <type_traits>
namespace std {
// =========================================================
// Relocatability classification:
//
// 0 = cannot relocate
// 1 = can relocate by memcpy/memmove
// 2 = can relocate by implementation-specific compiler built-in algorithm
// 3 = can relocate by static T::_Relocate() noexcept
// -3 = can relocate by static T::_Relocate() noexcept(false)
// 4 = can relocate by nothrow-move and destroy
// -4 = can relocate by move (may throw) and destroy
// 5 = can relocate by nothrow-copy and destroy
// -5 = can relocate by copy (may throw) and destroy
// 6 = can relocate by nothrow-default-construct, nothrow-assign, and destroy
// -6 = can relocate by default-construct (may throw), assign (may
throw), and destroy
// =========================================================
template<typename T>
consteval int classify_relocatability(void) noexcept
{
if constexpr ( requires(T *dst, T *src, size_t n){
T::_Relocate(dst, src, n); } )
{
return noexcept(T::_Relocate((T*)nullptr,(T*)nullptr,1uz)) ? 3 : -3;
}
else if constexpr ( false /* arm64e stuff */ ) return 2;
else if constexpr (is_trivially_copyable_v<T>) return 1; // memcpy/memmove
else if constexpr ( is_destructible_v<T> )
{
/**/ if constexpr (is_nothrow_move_constructible_v<T>) return 4;
else if constexpr (is_move_constructible_v<T> ) return -4;
else if constexpr (is_nothrow_copy_constructible_v<T>) return 5;
else if constexpr (is_copy_constructible_v<T> ) return -5;
else if constexpr
(is_nothrow_default_constructible_v<T>&&is_nothrow_assignable_v<T&,
T>) return 6;
else if constexpr
(is_default_constructible_v<T>&&is_assignable_v<T&, T>
) return -6;
}
else return 0;
}
template <typename T>
struct relocatability : integral_constant< int,
classify_relocatability<T>() > {};
template <typename T>
inline constexpr int relocatability_v = relocatability<T>::value;
template <typename T>
void relocate(T *const dst, T const *const src, size_t const n)
{
static_assert( is_object_v<T> );
static_assert( !is_const_v<T> && !is_volatile_v<T> );
constexpr int cat = relocatability_v<T>;
if constexpr ( 0 == cat )
{
static_assert( 0 != cat, "Type is not relocatable");
}
else if constexpr ( 1 == cat )
{
memmove( dst, src, n * sizeof(T) );
}
else if constexpr ( 2 == cat )
{
// arm64e stuff goes in here
}
else if constexpr ( 3 == cat || -3 == cat )
{
T::_Relocate(dst, src, n);
}
else if constexpr ( 4 == cat || -4 == cat )
{
for ( size_t i = 0uz; i < n; ++i )
{
construct_at( dst + i, move(src[i]) );
destroy_at( src + i );
}
}
else if constexpr ( 5 == cat || -5 == cat )
{
for ( size_t i = 0uz; i < n; ++i )
{
construct_at( dst + i, src[i] );
destroy_at( src + i );
}
}
else if constexpr ( 6 == cat || -6 == cat )
{
for ( size_t i = 0uz; i < n; ++i )
{
construct_at( dst + i );
dst[i] = move( src[i] );
destroy_at( src + i );
}
}
}
} // namespace std
#include <iostream>
struct A {};
struct B {
B(B&&) noexcept = default;
~B() = default;
};
struct C {
C(const C&) {}
~C() {}
};
struct D {
static void _Relocate(D*, D*, size_t) noexcept {}
};
struct E {
static void _Relocate(E*, E*, size_t) {} // may throw
};
int main()
{
std::cout << "A: " << std::relocatability<A>::value << "\n";
std::cout << "B: " << std::relocatability<B>::value << "\n";
std::cout << "C: " << std::relocatability<C>::value << "\n";
std::cout << "D: " << std::relocatability<D>::value << "\n";
std::cout << "E: " << std::relocatability<E>::value << "\n";
}
>
>
> How about if 'is_trivially_relocatable_v' were to be taken out and replaced with 'relocatability_v':
>
> namespace std {
> template<typename T>
> struct relocatability : integral_constant<int, . . . >{};
> }
Implementation might look something like this:
https://godbolt.org/z/M57xh1TvY
And here it is copy-pasted:
#include <cstddef>
#include <type_traits>
namespace std {
// =========================================================
// Relocatability classification:
//
// 0 = cannot relocate
// 1 = can relocate by memcpy/memmove
// 2 = can relocate by implementation-specific compiler built-in algorithm
// 3 = can relocate by static T::_Relocate() noexcept
// -3 = can relocate by static T::_Relocate() noexcept(false)
// 4 = can relocate by nothrow-move and destroy
// -4 = can relocate by move (may throw) and destroy
// 5 = can relocate by nothrow-copy and destroy
// -5 = can relocate by copy (may throw) and destroy
// 6 = can relocate by nothrow-default-construct, nothrow-assign, and destroy
// -6 = can relocate by default-construct (may throw), assign (may
throw), and destroy
// =========================================================
template<typename T>
consteval int classify_relocatability(void) noexcept
{
if constexpr ( requires(T *dst, T *src, size_t n){
T::_Relocate(dst, src, n); } )
{
return noexcept(T::_Relocate((T*)nullptr,(T*)nullptr,1uz)) ? 3 : -3;
}
else if constexpr ( false /* arm64e stuff */ ) return 2;
else if constexpr (is_trivially_copyable_v<T>) return 1; // memcpy/memmove
else if constexpr ( is_destructible_v<T> )
{
/**/ if constexpr (is_nothrow_move_constructible_v<T>) return 4;
else if constexpr (is_move_constructible_v<T> ) return -4;
else if constexpr (is_nothrow_copy_constructible_v<T>) return 5;
else if constexpr (is_copy_constructible_v<T> ) return -5;
else if constexpr
(is_nothrow_default_constructible_v<T>&&is_nothrow_assignable_v<T&,
T>) return 6;
else if constexpr
(is_default_constructible_v<T>&&is_assignable_v<T&, T>
) return -6;
}
else return 0;
}
template <typename T>
struct relocatability : integral_constant< int,
classify_relocatability<T>() > {};
template <typename T>
inline constexpr int relocatability_v = relocatability<T>::value;
template <typename T>
void relocate(T *const dst, T const *const src, size_t const n)
{
static_assert( is_object_v<T> );
static_assert( !is_const_v<T> && !is_volatile_v<T> );
constexpr int cat = relocatability_v<T>;
if constexpr ( 0 == cat )
{
static_assert( 0 != cat, "Type is not relocatable");
}
else if constexpr ( 1 == cat )
{
memmove( dst, src, n * sizeof(T) );
}
else if constexpr ( 2 == cat )
{
// arm64e stuff goes in here
}
else if constexpr ( 3 == cat || -3 == cat )
{
T::_Relocate(dst, src, n);
}
else if constexpr ( 4 == cat || -4 == cat )
{
for ( size_t i = 0uz; i < n; ++i )
{
construct_at( dst + i, move(src[i]) );
destroy_at( src + i );
}
}
else if constexpr ( 5 == cat || -5 == cat )
{
for ( size_t i = 0uz; i < n; ++i )
{
construct_at( dst + i, src[i] );
destroy_at( src + i );
}
}
else if constexpr ( 6 == cat || -6 == cat )
{
for ( size_t i = 0uz; i < n; ++i )
{
construct_at( dst + i );
dst[i] = move( src[i] );
destroy_at( src + i );
}
}
}
} // namespace std
#include <iostream>
struct A {};
struct B {
B(B&&) noexcept = default;
~B() = default;
};
struct C {
C(const C&) {}
~C() {}
};
struct D {
static void _Relocate(D*, D*, size_t) noexcept {}
};
struct E {
static void _Relocate(E*, E*, size_t) {} // may throw
};
int main()
{
std::cout << "A: " << std::relocatability<A>::value << "\n";
std::cout << "B: " << std::relocatability<B>::value << "\n";
std::cout << "C: " << std::relocatability<C>::value << "\n";
std::cout << "D: " << std::relocatability<D>::value << "\n";
std::cout << "E: " << std::relocatability<E>::value << "\n";
}
Received on 2025-10-30 00:05:28
