Date: Tue, 28 Oct 2025 17:47:42 +0500
> Again, this is potentially undefined behaviour.
Thats why it must be in standard library, others cannot do it without UB
About memcpy, it seems for you, that its not correct, because you're used
to the fact that it's wrong. But if you memswap ANY type twice into buffer
and into original place again any type, even self reference, will work. Its
impossible to break something with such memory manipulations
вт, 28 окт. 2025 г. в 17:23, Frederick Virchanza Gotham via Std-Proposals <
std-proposals_at_[hidden]>:
> On Tue, Oct 28, 2025 at 12:16 PM Frederick Virchanza Gotham wrote:
> >
> > Here's code I shared earlier in the week on this mailing list:
> >
> > https://godbolt.org/z/nbeKMKe1o
>
>
> Sorry, disregard my last post, as it only constructs another T in the
> place of an original T.
>
> The following code allows the construction of a different type in
> place of the original T:
>
> https://godbolt.org/z/oPKb977qb
>
> (Of course it assumes that the memory alignment is correct)
>
> And here it is copy-pasted:
>
> #include <cassert> // assert
> #include <cstddef> // byte
> #include <cstring> // memcpy
> #include <algorithm> // swap_ranges
> #include <memory> // construct_at, destroy_at
> #include <type_traits> // type traits
> #include <utility> // forward, move
>
> #ifdef NDEBUG
> # define CRASH_IN_DEBUG_MODE() /* nothing */
> #else
> # define CRASH_IN_DEBUG_MODE() assert(nullptr == "Destructor
> threw an exception!")
> #endif
>
> #if defined(HAS_STD_RELOCATE_AT) && defined(HAS_STD_IS_NOTHROW_RELOCATABLE)
> # define CAN_USE_STD_RELOCATE 1
> #else
> # define CAN_USE_STD_RELOCATE 0
> #endif
>
> template<typename T, typename U = T, typename... Args>
> requires (!std::is_const_v<T> && !std::is_volatile_v<T>)
> void replace(T *const where, Args&&... args)
> noexcept(std::is_nothrow_constructible_v<U, Args...>)
> {
> using std::construct_at;
> using std::destroy_at;
>
> if constexpr ( std::is_nothrow_constructible_v<U, Args...> )
> {
> try { destroy_at(where); } catch (...) { CRASH_IN_DEBUG_MODE(); }
> construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...);
> }
> #if CAN_USE_STD_RELOCATE
> else if constexpr ( true )
> {
> alignas(T) std::byte elsewhere[sizeof(T)];
> T *const elsewhereT =
> static_cast<T*>(static_cast<void*>(elsewhere));
>
> // Relocation is supported (non-standard extension)
> if constexpr ( std::is_nothrow_relocatable_v<T> )
> {
> std::relocate_at(where, elsewhereT);
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> std::relocate_at(elsewhereT, where);
> throw;
> }
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> }
> else if constexpr ( std::is_nothrow_move_constructible_v<T> )
> {
> // Relocation exists but isn't noexcept — fallback to move
> construct_at(elsewhereT, std::move(*where));
> try { destroy_at(where); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> construct_at(where, std::move(*elsewhereT));
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> throw;
> }
>
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> }
> else
> {
> static_assert( false == requires { typename
> T::tag_never_retain; } );
>
> using std::memcpy;
> std::byte elsewhere[sizeof(T)];
> memcpy(elsewhere, where, sizeof *elsewhere);
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> memcpy(where, elsewhere, sizeof *where);
> throw;
> }
>
> std::swap_ranges(
> elsewhere,
> elsewhere + sizeof elsewhere,
> static_cast<std::byte*>(static_cast<void*>(where))
> );
>
> try { destroy_at(where); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
>
> memcpy(where, elsewhere, sizeof *where);
> }
> }
> #endif // if CAN_USE_STD_RELOCATE
> else if constexpr (std::is_nothrow_move_constructible_v<T>)
> {
> // No relocation support; fallback to move construction
> alignas(T) std::byte elsewhere[sizeof(T)];
> T *const elsewhereT =
> static_cast<T*>(static_cast<void*>(elsewhere));
>
> construct_at(elsewhereT, std::move(*where));
> try { destroy_at(where); } catch (...) { CRASH_IN_DEBUG_MODE(); }
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> construct_at(where, std::move(*elsewhereT));
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> throw;
> }
>
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> }
> else
> {
> static_assert( false == requires { typename T::tag_never_retain; }
> );
>
> using std::memcpy;
> std::byte elsewhere[sizeof(T)];
> memcpy(elsewhere, where, sizeof *elsewhere);
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> memcpy(where, elsewhere, sizeof *where);
> throw;
> }
>
> std::swap_ranges(
> elsewhere,
> elsewhere + sizeof elsewhere,
> static_cast<std::byte*>(static_cast<void*>(where))
> );
>
> try { destroy_at(where); } catch (...) { CRASH_IN_DEBUG_MODE(); }
>
> memcpy(where, elsewhere, sizeof *where);
> }
> }
>
> #include <string>
>
> int main(void)
> {
> struct Monkey {
> Monkey(void) noexcept(false) = default;
> Monkey(Monkey&) = delete;
> Monkey(Monkey&&) = delete;
> typedef int tag_never_retain2; // try removing the '2' here
> };
>
> Monkey var;
> replace(&var);
>
> std::string str;
> replace(&str);
>
> replace<std::string,Monkey>(&str);
> }
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Thats why it must be in standard library, others cannot do it without UB
About memcpy, it seems for you, that its not correct, because you're used
to the fact that it's wrong. But if you memswap ANY type twice into buffer
and into original place again any type, even self reference, will work. Its
impossible to break something with such memory manipulations
вт, 28 окт. 2025 г. в 17:23, Frederick Virchanza Gotham via Std-Proposals <
std-proposals_at_[hidden]>:
> On Tue, Oct 28, 2025 at 12:16 PM Frederick Virchanza Gotham wrote:
> >
> > Here's code I shared earlier in the week on this mailing list:
> >
> > https://godbolt.org/z/nbeKMKe1o
>
>
> Sorry, disregard my last post, as it only constructs another T in the
> place of an original T.
>
> The following code allows the construction of a different type in
> place of the original T:
>
> https://godbolt.org/z/oPKb977qb
>
> (Of course it assumes that the memory alignment is correct)
>
> And here it is copy-pasted:
>
> #include <cassert> // assert
> #include <cstddef> // byte
> #include <cstring> // memcpy
> #include <algorithm> // swap_ranges
> #include <memory> // construct_at, destroy_at
> #include <type_traits> // type traits
> #include <utility> // forward, move
>
> #ifdef NDEBUG
> # define CRASH_IN_DEBUG_MODE() /* nothing */
> #else
> # define CRASH_IN_DEBUG_MODE() assert(nullptr == "Destructor
> threw an exception!")
> #endif
>
> #if defined(HAS_STD_RELOCATE_AT) && defined(HAS_STD_IS_NOTHROW_RELOCATABLE)
> # define CAN_USE_STD_RELOCATE 1
> #else
> # define CAN_USE_STD_RELOCATE 0
> #endif
>
> template<typename T, typename U = T, typename... Args>
> requires (!std::is_const_v<T> && !std::is_volatile_v<T>)
> void replace(T *const where, Args&&... args)
> noexcept(std::is_nothrow_constructible_v<U, Args...>)
> {
> using std::construct_at;
> using std::destroy_at;
>
> if constexpr ( std::is_nothrow_constructible_v<U, Args...> )
> {
> try { destroy_at(where); } catch (...) { CRASH_IN_DEBUG_MODE(); }
> construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...);
> }
> #if CAN_USE_STD_RELOCATE
> else if constexpr ( true )
> {
> alignas(T) std::byte elsewhere[sizeof(T)];
> T *const elsewhereT =
> static_cast<T*>(static_cast<void*>(elsewhere));
>
> // Relocation is supported (non-standard extension)
> if constexpr ( std::is_nothrow_relocatable_v<T> )
> {
> std::relocate_at(where, elsewhereT);
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> std::relocate_at(elsewhereT, where);
> throw;
> }
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> }
> else if constexpr ( std::is_nothrow_move_constructible_v<T> )
> {
> // Relocation exists but isn't noexcept — fallback to move
> construct_at(elsewhereT, std::move(*where));
> try { destroy_at(where); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> construct_at(where, std::move(*elsewhereT));
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> throw;
> }
>
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> }
> else
> {
> static_assert( false == requires { typename
> T::tag_never_retain; } );
>
> using std::memcpy;
> std::byte elsewhere[sizeof(T)];
> memcpy(elsewhere, where, sizeof *elsewhere);
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> memcpy(where, elsewhere, sizeof *where);
> throw;
> }
>
> std::swap_ranges(
> elsewhere,
> elsewhere + sizeof elsewhere,
> static_cast<std::byte*>(static_cast<void*>(where))
> );
>
> try { destroy_at(where); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
>
> memcpy(where, elsewhere, sizeof *where);
> }
> }
> #endif // if CAN_USE_STD_RELOCATE
> else if constexpr (std::is_nothrow_move_constructible_v<T>)
> {
> // No relocation support; fallback to move construction
> alignas(T) std::byte elsewhere[sizeof(T)];
> T *const elsewhereT =
> static_cast<T*>(static_cast<void*>(elsewhere));
>
> construct_at(elsewhereT, std::move(*where));
> try { destroy_at(where); } catch (...) { CRASH_IN_DEBUG_MODE(); }
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> construct_at(where, std::move(*elsewhereT));
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> throw;
> }
>
> try { destroy_at(elsewhereT); } catch (...) {
> CRASH_IN_DEBUG_MODE(); }
> }
> else
> {
> static_assert( false == requires { typename T::tag_never_retain; }
> );
>
> using std::memcpy;
> std::byte elsewhere[sizeof(T)];
> memcpy(elsewhere, where, sizeof *elsewhere);
>
> try { construct_at(reinterpret_cast<U*>(where),
> std::forward<Args>(args)...); }
> catch (...)
> {
> memcpy(where, elsewhere, sizeof *where);
> throw;
> }
>
> std::swap_ranges(
> elsewhere,
> elsewhere + sizeof elsewhere,
> static_cast<std::byte*>(static_cast<void*>(where))
> );
>
> try { destroy_at(where); } catch (...) { CRASH_IN_DEBUG_MODE(); }
>
> memcpy(where, elsewhere, sizeof *where);
> }
> }
>
> #include <string>
>
> int main(void)
> {
> struct Monkey {
> Monkey(void) noexcept(false) = default;
> Monkey(Monkey&) = delete;
> Monkey(Monkey&&) = delete;
> typedef int tag_never_retain2; // try removing the '2' here
> };
>
> Monkey var;
> replace(&var);
>
> std::string str;
> replace(&str);
>
> replace<std::string,Monkey>(&str);
> }
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2025-10-28 12:47:54
