Date: Tue, 28 Oct 2025 13:59:12 +0100
No, the memory can be used to communicate with the hardware or special flags (e.g. non-pageable) were assigned with the help of the operating system. The standard library should not move non-moveable arbitrary objects around.
I have not even started talking about multi-threaded use.
-----Ursprüngliche Nachricht-----
Von:Nikl Kelbon via Std-Proposals <std-proposals_at_[hidden]>
Gesendet:Di 28.10.2025 13:48
Betreff:Re: [std-proposals] Way to get rid of valueless_by_exception in std::variant
An:std-proposals_at_[hidden];
CC:Nikl Kelbon <kelbonage_at_[hidden]>;
> 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] <mailto: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] <mailto:Std-Proposals_at_[hidden]>
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2025-10-28 13:12:25
