C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Way to get rid of valueless_by_exception in std::variant

From: Nikl Kelbon <kelbonage_at_[hidden]>
Date: Tue, 28 Oct 2025 18:17:03 +0500
We talk about movable types, in cases when move constructor may throw
exception

вт, 28 окт. 2025 г. в 18:12, Sebastian Wittmeier via Std-Proposals <
std-proposals_at_[hidden]>:

> 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]>:
>
> 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
>
> --
> Std-Proposals mailing list
> 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:17:15