C++ Logo


Advanced search

Re: [ub] Draft 2 of Enhanced C/C++ memory and object model

From: Niall Douglas <s_sourceforge_at_[hidden]>
Date: Thu, 28 Mar 2019 10:41:50 +0000
> meant "... AND trivially detachable"; but I recommend just eliminating
> this green text entirely. */All/* objects — even objects of
> non-trivially attachable types — continue to occupy contiguous bytes of
> storage, IIUC.

Thanks for the catch on the or vs and.

For me personally, I think that the time has come for vptr-based "plain"
virtual function dispatch to be anointed as THE implementation. Then we
can expand the category of bit copyable types significantly, to the
great long term benefit of the ecosystem.

Just to be clear, by this I specifically mean the most common case of
there being a single vptr to a single vtable containing all the virtual
function implementations for that type.

I do not mean the situation where there are virtual base classes, which
is usually implemented by adding a second vbptr or second (or third) vptr.

I appreciate that this will not be a popular opinion. However, single
vptr types are C-compatible, and I think it might be possible to
persuade WG14 to have C support the "plain" vptr-based dispatch. We then
can draw a line around that as "C support". This would be in keeping
with closer C and C++ cooperation on Herbceptions, as well.

> Avoid using the phrase "can be" in normative text. Probably what you
> mean is that the implementation is /*required*/ to define "reachable C++
> program" and /*required*/ to permit concurrent access to pages; but
> taken literally, all this text is saying is that the implementation
> /*can*/ do those things (it doesn't have to).

Thanks for the tip.

> An object of type T shall have /trivial/ attachment or detachment if
> [...]
> I think you meant to use the phrasing "An object of type T /*is said
> to*/ have /trivial /attachment if..." Otherwise, you're putting a
> requirement on (somebody) that T have trivial attachment, without ever
> having defined what that means.
> Separately, you seem to be defining "trivial attachment" as the opposite
> of "non-vacuous attachment," is that right? If so, that's unnecessarily
> confusing. The opposite of "non-vacuous" should be "vacuous," and the
> opposite of "trivial" should be "non-trivial."


> Your `auto in_place_attach<T>(span<byte>) -> span<T>` is extremely
> reminiscent of P0593R3
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0593r3.html#direct-object-creation>'s
> not-yet-proposed-but-likely-to-get-proposed `auto bless<T>(void *p) ->
> T*`. It would be nice to compare and contrast what's going on there, or
> even try to harmonize the two papers.

There are also similarities to std::launder as well.

Both items will get added to the FAQ for Cologne. But it's been a lack
of time issue. I needed to get something ready for the WG14 meeting
deadline this week, and draft 2 already took me a month of mornings
before work. I can only work very slowly, unfortunately.

> As trivially attachable and detachable types have default attachment
> and detachment implementations in this proposal, that would mean
> that all types without pointers and references within them can now
> be relocated. This substantially expands the number of types whose
> move constructor and move assignment could be defaulted, and whose
> representation can be transported in CPU registers instead of on the
> stack.
> I think I agree with the gist of this section, but I don't understand
> what you mean by "This substantially expands the number of types whose
> /*move constructor and move assignment could be defaulted*/."
> Relocation-by-detachment-and-attachment has nothing to do with
> move-construction, let alone move-assignment.

I'm saying that if you default your move constructor and assignment,
that under this proposal lots more situations can get automatically
implemented. In other words, if the type is trivially attachable and
detachable, by default it is now also trivially movable, and trivially
relocatable. So the programmer need no longer do anything, just default
the special members.

> struct file_descriptor {
> int fd = -1;
> file_descriptor(file_descriptor&& rhs) noexcept : fd(rhs.fd) {
> rhs.fd = -1; }
> ~file_descriptor() { if (fd != -1) close(fd); }
> };
> This `struct file_descriptor` should be "trivially relocatable" by
> anyone's definition. But it obviously can't have a defaulted
> move-constructor, because it does not have a defaulted destructor. RAII
> types must still follow the Rule of Three / Rule of Five. And
> higher-level types can follow the Rule of Zero. Higher-level types
> following the Rule of Zero will already default all their special
> members and thus do not gain anything in that department from being
> "trivially attachable and/or detachable."

Sure, if someone has written a custom move constructor, then nothing
changes. Backwards source compatibility after all.

But if they write, under this proposal, the following:

     struct file_descriptor {
         int fd = -1;
         file_descriptor(file_descriptor&&) = default;
         ~file_descriptor() { if (fd != -1) close(fd); }

... would now work. This is because the type contains no pointers nor
references, so it is trivially detachable and attachable. So, a
defaulted move constructor can trivially detach and reattach the type.
Which means moves become trivial, despite the non-trivial destructor.

Obviously this radically changes how we currently conceptualise moves in
terms of lifetime. I am not pretending that this isn't a hugely
disruptive proposal. But I do think that the gains are worth the pain,
especially long term.

I am sure there are lots of other interesting corner case changes which
would occur to the abstract machine if something like this proposal were
accepted into the standard. I also recognise that it will take many
meetings to work through those issues before SG12 can be confident that
this is not a really bad idea.


Received on 2019-03-28 11:42:13