C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Replace an object -- but retain old object if new object fails to construct

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
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";
}

Received on 2025-10-30 00:05:28