C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relocating destructor and operator reloc

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Sat, 29 Jun 2024 09:05:38 -0500
On Fri, Jun 28, 2024 at 11:31 AM 李 秋逸 via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Sorry for bother you. Seeing some proposals about relocating in C++, I
> think I found a easy and flexible way to do this with least changes to the
> core feature of C++ language. You can check it in the attachment or click *https://github.com/YandereChan2/Relocating-destructor-and-operator-reloc/blob/main/Operator%20reloc%20and%20relocating%20destructor.md
> <https://github.com/YandereChan2/Relocating-destructor-and-operator-reloc/blob/main/Operator%20reloc%20and%20relocating%20destructor.md>*
> .
>
The core idea is introduce a new destructor T ~T(int), and with the help of
> RVO/NRVO to relocate an object from one address to the address of the
> return value.
>

Section 4 is conspicuously missing any example of a class with a
non-trivial data member *and* a non-trivial relocating destructor.
Of course programmers should *avoid* writing any such type, as a stylistic
preference; but you need to explain how it would work.

    struct S {
        ~S();
        S ~S(int);
    };

    struct Composite {
        S s_;
        int *p_;
        Composite(int *p) : s_(), p_(p) {}
        Composite(Composite&& rhs) : s_(std::move(rhs.s_)),
p_(std::exchange(rhs.p_, nullptr)) {}
        Composite& operator=(Composite&& rhs) { s_ = std::move(rhs.s_); p_
= std::exchange(rhs.p_, nullptr); return *this; }
        ~Composite() { delete p_; }

        Composite ~Composite(int) {
            Composite copy = std::move(*this); // How do I work
`reloc(this->s_)` into this code?
            return copy; // I don't want s_'s destructor to run here,
because I've already relocated out of it; but p_'s (trivial) destructor
needs to run as usual.
        }
    };

In writing this example, it also occurred to me that your syntax
`NameOfType ~NameOfType(int)` is pretty repetitive.
P2952 (to be seen by EWG sometime in the next ~6 months) might help you
here, but maybe this is also a sign that you should look for a simpler
syntax.

What happens if I write some other type there?
    std::any ~MyType(int) { ~~~~ } // Relocate myself into a std::any?
This is the first corollary to Murphy's Law: If you don't want people
writing that, then maybe you should find a syntax that makes it impossible
to write that. (See also "Make invalid states unrepresentable"
<https://blog.janestreet.com/effective-ml-revisited/>.)

A hot topic right now is whether trivial relocation can be used for swap.
(P2786 says "no"; P1144 says "yes"; most industry code says "of course
yes"; and you assume the answer is "yes" as well.) For P1144, this isn't a
problem, because obviously if an object's value follows its object
representation, then you can *by definition* swap two values by swapping
their object representations — and P1144 defines "trivially relocatable" as
a holistic property (analogous to "trivially copyable") *specifically
meaning* that an object's value follows its object representation. So
there's no problem with std::swap in P1144.
(Now, P1144 does leave the *implementation* of "swapping object
representations" up to the STL implementation. Everyone just uses either
`memcpy` or `__builtin_memcpy` to copy object representations today, e.g.
inside std::copy, even for non-TC types. Compiler vendors are technically
allowed to add more ceremony around copying object representations, and
force STL vendors to use those ceremonies — but thankfully no compiler
vendor has felt the desire to do so, yet.)

But in your proposal for *non-trivial* relocation, you aren't just swapping
object representations in an unspecified vendor-specific manner. The only
way *you* can relocate an object's value is by calling its relocating
destructor, which by definition *destroys* the object. So when *you*
propose that std::swap(a,b) should use relocation, you're proposing that
std::swap(a,b) should *destroy both a and b* and then re-construct them. So
the `a` object that exists after a swap isn't the same `a` object that
existed before the swap. Not just "same object with different value," but
literally *it is now a different object*. That's the same problem that
P2786 has with swap — except that whereas P2786 says "okay, so we can't
optimize swap for TR types," you just seem to be ignoring the problem.
You'll have to tackle it head-on, because right now it looks like a
complete roadblock for your proposal.

my $.02,
Arthur

Received on 2024-06-29 14:05:52