CopyPastor

Detecting plagiarism made easy.

Score: 1; Reported for: Exact paragraph match Open both answers

Possible Plagiarism

Reposted on 2026-08-06
by Davislor

Original Post

Original - Posted on 2026-07-18
by Davislor



            
Present in both answers; Present only in the new answer; Present only in the old answer;

Some of the language-lawyers here would argue that this example code has UB, although I disagree. Let’s first look at an example from the C++23 Standard itself:
```lang-c++ template<typename ...T> struct AlignedUnion { alignas(T...) unsigned char data[max(sizeof(T)...)]; }; int f() { AlignedUnion<int, char> au; int *p = new (au.data) int; // OK, au.data provides storage char *c = new (au.data) char(); // OK, ends lifetime of *p char *d = new (au.data + 1) char(); return *c + *d; // OK }
struct A { unsigned char a[32]; }; struct B { unsigned char b[16]; }; A a; B *b = new (a.a + 8) B; // a.a provides storage for *b int *p = new (b->b + 4) int; // b->b provides storage for *p // a.a does not provide storage for *p (directly), // but *p is nested within a (see below) ```
This illustrates a clause from [intro.object] that reads in part:
> If a complete object is created in storage associated with another object e of type “array of N `unsigned char`” or of type “array of N `std::byte`”, that array provides storage for the created object if: > * the lifetime of *e* has begun and not ended, and > * the storage for the new object fits entirely within *e*, and > * there is no array object that satisfies these constraints nested within *e*
So to summarize a bit, the line `new (mem) unsigned char[sizeof(T)]` creates and zero-initializes an array of `unsigned char`in the storage pointed to by `mem`. Based on what you said about `T` being an implicit-lifetime type, the applicable clause is,
> For each operation that is specified as implicitly creating objects, that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types in its specified region of storage if doing so would result in the program having defined behavior
Which is followed by
> An operation that begins the lifetime of an array of unsigned char or std::byte implicitly creates objects within the region of storage occupied by the array.
Therefore, the invocation of `operator new` creates and starts the lifetimes of implicit-lifetime objects in the storage occupied by `bytes`.
Elsewhere, we see that `reinterpret_cast` between pointers to unrelated types is specified as a `static_cast` to `voir*` and then a `static_cast` from `void*` to the destination type. The relevant clause under [expr.static.cast]/14 is
> Otherwise, if the original pointer value points to an object *a*, and there is an object b of type similar to T that is pointer-interconvertible with *a*, the result is a pointer to (b*.
Since we created an object of implicit-lifetime type `T` at the address `bytes[0]` and started its lifetime, this returns a valid pointer to that `T`.
Furthermore, this code does not violate the strict-aliasing rules, as the only pointers aliasing `ptr` are `mem`, which is a pointer to `void`, and ``bytes`, which is a reference to character type.

### Potential UB
The line `new (mem) unsigned char[sizeof(T)]` default-initializes the array object (\[expr.new\]/23), which initializes the bytes of storage to zeroes. Then, the `reinterpret_cast`returns a pointer to a zero-initialized object (presuming `mem` and `T` meet all the requirements, which isn’t checked). A zero-initialized `T` is not necessarily valid, and might even be a trap representation. This is not the behavior specified for `std::start_lifetime_as`, which returns an object as if converted by `std::bit_cast` on the object representation.
Although there’s no violation of strict aliasing within this function body, it is possible that the operation is replacing an object that is not transparently replaceable, that is still aliased by another pointer of a different type, or to a base-class object or a complete `const` object. In those cases, you would need `std::launder` on the return value to tell the compiler not to assume the destroyed object is still there.

### Other Interpretations
So there is some ambiguity here about whether the objects of implicit-lifetime type created by `operator new unsigned char[]` are only of the `unsigned char` or `std::byte` elements, or of any arbitrary implicit-lifetime type. All actually-existing compilers interpret it the latter way. This makes it possible to implement an arena allocator, `std::start_lifetime_as` `malloc()`, a device driver that works with absolute memory addresses, and several other useful things without a magic compiler extension, and is certainly what the committee intended.
Another bit of quibbling is over the line about “returning a pointer to a *suitable created object*,” which might be introducing the definition that immediately follows, but some people here interpret as magic words that must appear in the specification exactly for the operation to be legal. In that case, though, `std::start_lifetime_as` never says the pointer it returns is to a *suitable created object*, so even on that interpretation, it’s fine if our version doesn’t either, making the whole issue irrelevant.
Finally, some people will quote a genuine oversight in the Standard: the definition of “transparently replaceable” objects does not mention the array object providing storage, which there’s a corregendum in the pipeline about. A valid point in theory, but the examples in the Standard do in fact a;ready say that replacing a range of bytes in an array of `unsigned char` by another object is “OK.”
Another variation I’ve heard is that the examples in the Standard only use placement `new` to crate objects in storage, which is one of the only operations directly stated to return a pointer to a *suitable created object*, and therefore conclude that the pointer returned by `reinterpret_cast` is not to a suitable object, although I’m mot sure what it isn’t suitable for.
Example 1 under \[intro.object\] is one of several examples in the C++23 Standard that say no to your third question. This directly says it’s “OK” to create an object within an array of `unsigned char` and dereference a pointer to it, without `std::launder`.
```lang-cpp template<typename ...T> struct AlignedUnion { alignas(T...) unsigned char data[max(sizeof(T)...)]; };
int f() { AlignedUnion<int, char> au; int *p = new (au.data) int; // OK, au.data provides storage char *c = new (au.data) char(); // OK, ends lifetime of *p char *d = new (au.data + 1) char(); return *c + *d; // OK }
struct A { unsigned char a[32]; }; struct B { unsigned char b[16]; }; A a; B *b = new (a.a + 8) B; // a.a provides storage for *b int *p = new (b->b + 4) int; // b->b provides storage for *p // a.a does not provide storage for *p (directly), // but *p is nested within a (see below) ```
The reason that `c` and `d` are “OK” to use without `std::launder`, even though `int` is not transparently replaceable by `char`, is presumably that a `char*` is allowed to alias an object of any other type, so the compiler is not permitted to optimize the expressions `*c` or `*d` on the assumption that they do not alias `b`, even without `std::launder`.(Note that a `char*` aliasing an `int*` is different from an `int*` aliasing bytes of an array of `unsigned char`. An array of `char` does not provide storage for sub-objects in the same way as an array of `unsigned char` or `std::byte`, and pointers of other types may not freely alias it.)
If the object `*b` were replaced by some type other than a character type or `std::byte`, the strict-aliasing rules would let the compiler optimize on the assumption that the original object is still at that location and a pointer of some other type cannot alias it. You would then need `std::launder` to disable these erroneous optimizations.
Another debate is over the line, “some operations are described as producing a pointer to a *suitable created object*” (also under \[intro.object\]). If this is merely a definition for the following sentence, “these operations select one of the implicitly-created objects whose address is the address of the start of the region of storage, and produce a pointer value that points to that object, if that value would result in the program having defined behavior,” it follows that it is legal to `reinterpret_cast` a pointer from storage to a pointer to an implicit-lifetime type and dereference it. If, however, only sections of the Standard that use the specific magic phrase “pointer to a suitable created object” are allowed, that limits you to getting object pointers from `new`, untyped memory returned from C allocation functions, or allocated by memory resources.
Either way, using `std::launder` around a `reinterpret_Cast` except in one of the enumerated special cases is nothing but superstition, since the Standard never says `std::launder` has anything to do with a pointer to a suitable created object either. The function intended to be used for this purpose is `std::start_lifetime_as`, but this is not formally described in C++23 as returning a pointer to a *suitable created object* (and neither is `std::bit_Cast`, which that section references).

        
Present in both answers; Present only in the new answer; Present only in the old answer;