C++ Logo

std-proposals

Advanced search

Re: P1839 and the object representation of subobjects

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Tue, 21 Jul 2020 19:38:40 -0400
On Tue, Jul 21, 2020 at 5:31 PM Giuseppe D'Angelo via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
> Il 21/07/20 17:01, Jason McKesson via Std-Proposals ha scritto:
> > We technically already have a way to do that: `std::launder`:
> >
> > ```
> > auto byte_ptr = reinterpret_cast<byte *>(qoptr);
> > auto c1ptr = std::launder(reinterpret_cast<C1 *>(byte_ptr + off);
> > ```
> >
> > If there is an object of type `C1` at the given address, then
> > `std::launder` will return a pointer to it. This is the purpose of
> > `launder`.
>
> I'm not 100% sure that P1839 would allow for the above reintepret_cast,
> though. And if it does, would std::launder still be necessary?

I'm not sure what you mean, as there are two `reinterpret_cast`s, and
both of them are 100% legal even in C++98. You can always do that kind
of reinterpret_cast. It's the *use of the pointers* whose legality is
questioned.

The parts that are illegal in C++ as it currently stands are:

1: `byte_ptr + off`. Pointer arithmetic in C++ is based on arrays
(which for a single object is treated as a 1-element array).
`byte_ptr` does not point to an array of `char`, so pointer arithmetic
is invalid.
2: The pointer resulting from `reinterpret_cast<C1*>` does not
*actually* point to the subobject of type `C1` within the other
object, even if the address `qoptr + off` is the address of that
subobject.

P1839R2 solves problem number 1, as stated by its wording:

> [intro.object]/2 (inserted)
> The object representation of an object `a` of type cv T is a sequence
> of N cv unsigned char objects that occupy the same storage as `a`,
> where N is equal to sizeof(T). The sequence is considered to be
> an array of N T if the object occupies contiguous bytes of storage

> [reinterpret.cast]/7.2 (modified)
> If `a` occupies contiguous bytes of storage and T2 is unsigned char, char or std::byte, the result is a pointer to the first element of the object representation of `a`

So it's quite clear that `byte_ptr` points to the first element of
`qoptr`'s object representation. And it's clear that this object
representation is an array of bytes if `qoptr` points to a type that
is contiguous.

Note that the proposal P1945 is the part that expands "object occupies
contiguous bytes of storage" to (many) more types than would currently
be the case.

As for #2, `std::launder` from C++17 solves this problem:

> [ptr.launder]/3
> Requires: `p` represents the address `A` of a byte in memory. An
> object X that is within its lifetime (6.8) and whose type is similar (7.5)
> to T is located at the address `A`. All bytes of storage that would be
> reachable through the result are reachable through `p` (see below).
>
> Returns: A value of type T* that points to `X`.

There is an object of type `C1` at the address `byte_ptr + off`;
therefore, `launder` retrieves a pointer to it.

Received on 2020-07-21 18:42:09