Date: Wed, 22 Oct 2025 14:13:48 -0400
On Wed, Oct 22, 2025 at 1:40 PM Thiago Macieira via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On Wednesday, 22 October 2025 10:15:01 Pacific Daylight Time Frederick
> Virchanza Gotham via Std-Proposals wrote:
> > So I understand what relocation is . . . and I understand that some
> classes
> > will have their methods malfunction if an object is moved to a different
> > address.
>
> Which is a sufficient condition for what you want.
>
Part of the confusion here, I think, is because Thiago said (and continues,
sometimes, to say) "relocatable" when he meant specifically "*trivially*
relocatable."
You can do the replacement operation like this:
template<class T, class... Args>
void replace(T *where, Args&&... args) {
static_assert(std::is_nothrow_destructible_v<T>);
if (std::is_nothrow_constructible_v<T, Args...>) {
* std::destroy_at(where);*
* std::construct_at(where, std::forward<Args>(args)...);*
} else if (std::is_nothrow_relocatable_v<T, Args...>) {
* alignas(T) char elsewhere[sizeof(T)];*
* std::relocate_at(where, (T*)elsewhere);*
* try {*
* std::construct_at(where, std::forward<Args>(args)...);*
* } catch (...) {*
* std::relocate_at((T*)elsewhere, where);*
* throw;*
* }*
* std::destroy_at((T*)elsewhere);*
} else {
// We can't really do it in a nice way, but we can use FVG's
approach:
* char elsewhere[sizeof(T)];*
* std::memcpy(elsewhere, where, sizeof(T));*
* try {*
* std::construct_at(where, std::forward<Args>(args)...);*
* } catch (...) {*
* std::memcpy(where, elsewhere, sizeof(T));*
* throw;*
* }*
* char yetagain[sizeof(T)];*
* std::memcpy(yetagain, where, sizeof(T));*
* std::memcpy(where, elsewhere, sizeof(T));*
* std::destroy_at(where);*
* std::memcpy(where, yetagain, sizeof(T));*
}
}
`is_nothrow_relocatable` is a strictly weaker condition than
`is_trivially_relocatable`: all trivially relocatable types are nothrow
relocatable.
If relocation can throw, then you have to use the third-and-ugliest
approach above, which relies on the idea that you can memcpy the
representation out of an object, then memcpy it back (to the *exact same
address*), and continue to use the object. This is true on all platforms
I'm aware of, but technically not guaranteed by the Standard.
If the object is actually (nothrow) *relocatable*, then you don't have to
memcpy it back to the exact same address in order to destroy it safely. You
can simply destroy the relocated copy (at its new address) instead of the
original. This is the second-and-relatively-prettier approach above.
> I don't see how my byte-swapping strategy has anything to do with
> > relocatability. Let me use a class as an example:
>
> Byte-swapping *IS* relocation. That's the whole point of the feature.
>
No, byte-swapping is *trivial* relocation.
(At least, it is in a sane world, and hopefully the large number of NB
comments on trivial relocation
<https://quuxplusone.github.io/blog/2025/10/12/nb-comments/#c-26-is-notable-for-the-large-nu>
will get us to that sane world in the end. The C++26 CD is not sane.)
–Arthur
std-proposals_at_[hidden]> wrote:
> On Wednesday, 22 October 2025 10:15:01 Pacific Daylight Time Frederick
> Virchanza Gotham via Std-Proposals wrote:
> > So I understand what relocation is . . . and I understand that some
> classes
> > will have their methods malfunction if an object is moved to a different
> > address.
>
> Which is a sufficient condition for what you want.
>
Part of the confusion here, I think, is because Thiago said (and continues,
sometimes, to say) "relocatable" when he meant specifically "*trivially*
relocatable."
You can do the replacement operation like this:
template<class T, class... Args>
void replace(T *where, Args&&... args) {
static_assert(std::is_nothrow_destructible_v<T>);
if (std::is_nothrow_constructible_v<T, Args...>) {
* std::destroy_at(where);*
* std::construct_at(where, std::forward<Args>(args)...);*
} else if (std::is_nothrow_relocatable_v<T, Args...>) {
* alignas(T) char elsewhere[sizeof(T)];*
* std::relocate_at(where, (T*)elsewhere);*
* try {*
* std::construct_at(where, std::forward<Args>(args)...);*
* } catch (...) {*
* std::relocate_at((T*)elsewhere, where);*
* throw;*
* }*
* std::destroy_at((T*)elsewhere);*
} else {
// We can't really do it in a nice way, but we can use FVG's
approach:
* char elsewhere[sizeof(T)];*
* std::memcpy(elsewhere, where, sizeof(T));*
* try {*
* std::construct_at(where, std::forward<Args>(args)...);*
* } catch (...) {*
* std::memcpy(where, elsewhere, sizeof(T));*
* throw;*
* }*
* char yetagain[sizeof(T)];*
* std::memcpy(yetagain, where, sizeof(T));*
* std::memcpy(where, elsewhere, sizeof(T));*
* std::destroy_at(where);*
* std::memcpy(where, yetagain, sizeof(T));*
}
}
`is_nothrow_relocatable` is a strictly weaker condition than
`is_trivially_relocatable`: all trivially relocatable types are nothrow
relocatable.
If relocation can throw, then you have to use the third-and-ugliest
approach above, which relies on the idea that you can memcpy the
representation out of an object, then memcpy it back (to the *exact same
address*), and continue to use the object. This is true on all platforms
I'm aware of, but technically not guaranteed by the Standard.
If the object is actually (nothrow) *relocatable*, then you don't have to
memcpy it back to the exact same address in order to destroy it safely. You
can simply destroy the relocated copy (at its new address) instead of the
original. This is the second-and-relatively-prettier approach above.
> I don't see how my byte-swapping strategy has anything to do with
> > relocatability. Let me use a class as an example:
>
> Byte-swapping *IS* relocation. That's the whole point of the feature.
>
No, byte-swapping is *trivial* relocation.
(At least, it is in a sane world, and hopefully the large number of NB
comments on trivial relocation
<https://quuxplusone.github.io/blog/2025/10/12/nb-comments/#c-26-is-notable-for-the-large-nu>
will get us to that sane world in the end. The C++26 CD is not sane.)
–Arthur
Received on 2025-10-22 18:14:07
