Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

"2020-02 Prague ISO C++ Committee Trip Report — 🎉 C++20 is Done! 🎉"

191 views
Skip to first unread message

Lynn McGuire

unread,
Feb 19, 2020, 5:08:24 PM2/19/20
to
"2020-02 Prague ISO C++ Committee Trip Report — 🎉 C++20 is Done! 🎉"

https://www.reddit.com/r/cpp/comments/f47x4o/202002_prague_iso_c_committee_trip_report_c20_is/

Wow, that is a lot of new stuff that I probably will not use.

Lynn

Jorgen Grahn

unread,
Feb 19, 2020, 5:21:54 PM2/19/20
to
I wonder what happened to Stroustrup's request to slow down the
development? I read no C++-related news, so I am unaware of any
results.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Juha Nieminen

unread,
Feb 23, 2020, 9:08:52 AM2/23/20
to
Jorgen Grahn <grahn...@snipabacken.se> wrote:
> I wonder what happened to Stroustrup's request to slow down the
> development?

No major developments have been made since C++11.
Both C++14 and C++17 were relatively minor updates.

How much more slow does it need to be? If no major updates are
done in 9 years, and that's *still* too fast, then what is the
proper timescale in that case? 20 years? 50 years? What?

Daniel

unread,
Feb 23, 2020, 12:07:38 PM2/23/20
to
On Sunday, February 23, 2020 at 9:08:52 AM UTC-5, Juha Nieminen wrote:
> Jorgen Grahn <grahn...@snipabacken.se> wrote:
> > I wonder what happened to Stroustrup's request to slow down the
> > development?
>
> No major developments have been made since C++11.
> Both C++14 and C++17 were relatively minor updates.
>
I don't care about big updates, I care about small updates, in particular, I
care about small updates that would make C++ more suitable as a language to
write libraries that convert standard conforming data formats such as ODBC
results or CBOR bytes into strongly typed C++ data structures. And for the
most part, that isn't happening. No int128_t, big integer, big decimal, big
float ... On the other hand, std::endian is welcome.

Daniel

Chris Vine

unread,
Feb 23, 2020, 12:46:47 PM2/23/20
to
On Sun, 23 Feb 2020 14:08:41 -0000 (UTC)
Juha Nieminen <nos...@thanks.invalid> wrote:
> Jorgen Grahn <grahn...@snipabacken.se> wrote:
> > I wonder what happened to Stroustrup's request to slow down the
> > development?
>
> No major developments have been made since C++11.
> Both C++14 and C++17 were relatively minor updates.

C++14 and C++17 may have been minor updates, but concepts, modules and
coroutines are definitely not minor. Arguably ranges aren't either.

Daniel

unread,
Feb 23, 2020, 1:54:00 PM2/23/20
to
On Sunday, February 23, 2020 at 12:46:47 PM UTC-5, Chris Vine wrote:
>
> C++14 and C++17 may have been minor updates, but concepts, modules and
> coroutines are definitely not minor. Arguably ranges aren't either.

Yes, but to what end? Open sources libraries like rust serde or java jackson
aren't even possible in C++, because C++ doesn't have the types.

Daniel

Chris Vine

unread,
Feb 23, 2020, 2:40:58 PM2/23/20
to
I'm not arguing with you. I suspect C++ will founder under its own
weight (apart from the attractions of other languages such as rust,
which has greater regularity and type safety).

Many programmers will not have the time to keep up with the sheer
complexity of C++20. The recent willingness of the C++ standard to
break previously working code is also unfortunate: another thread has
revealed something that I didn't know, namely that in C++17, because of
new lifetime rules, if you have aligned static storage in a buffer of
type char[] (the type which most aligned uninitialized storage will end
up being) you have to use std::launder to access objects in the buffer
through the buffer pointer. Everyone had previously taken it that if
you construct objects in an aligned buffer using placement new and
used the appropriate cast acceptable to C++'s aliasing rules to access
them, you were good to go. Sadly not. It's enough to make you weep.

Richard

unread,
Feb 24, 2020, 1:08:20 AM2/24/20
to
[Please do not mail me a copy of your followup]

Lynn McGuire <lynnmc...@gmail.com> spake the secret code
<r2kbkd$66s$2...@dont-email.me> thusly:

>"2020-02 Prague ISO C++ Committee Trip Report — 🎉 C++20 is Done! 🎉"
>
>https://www.reddit.com/r/cpp/comments/f47x4o/202002_prague_iso_c_committee_trip_report_c20_is/
>
>Wow, that is a lot of new stuff that I probably will not use.

Based on your comments in the past, this is what I expect you will
use (but maybe only indirectly):

- modules: who doesn't want a faster build?
- concepts: if only as a user of the standard library, for better
template error messages
- ranges: aren't you tired of typing c.begin(), c.end() as
arguments all the time?
- constexpr: really handy for writing code evaluated at compile
time instead of wacky template metaprogramming
- std::format: everyone complains about the speed and usability of
stream formatting
- operator<=>: makes it easier to write comparable types
- std::span: type safe C-style arrays
- std::source_location: one more nail in the preprocessor coffin

That's pretty much most of the features on that list. What do you
expect you wouldn't actually use from the above list?
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Richard

unread,
Feb 24, 2020, 1:10:18 AM2/24/20
to
[Please do not mail me a copy of your followup]

Daniel <daniel...@gmail.com> spake the secret code
<1b3f5510-073f-421d...@googlegroups.com> thusly:
It would help if you provided specific examples because I doubt
most people here are familiar with those libraries.

Pavel

unread,
Feb 24, 2020, 1:22:32 AM2/24/20
to
Chris Vine wrote:
> On Sun, 23 Feb 2020 10:53:51 -0800 (PST)
> Daniel <daniel...@gmail.com> wrote:
>> On Sunday, February 23, 2020 at 12:46:47 PM UTC-5, Chris Vine wrote:
>>>
>>> C++14 and C++17 may have been minor updates, but concepts, modules and
>>> coroutines are definitely not minor. Arguably ranges aren't either.
>>
>> Yes, but to what end? Open sources libraries like rust serde or java jackson
>> aren't even possible in C++, because C++ doesn't have the types.
>
> I'm not arguing with you. I suspect C++ will founder under its own
> weight (apart from the attractions of other languages such as rust,
> which has greater regularity and type safety).
>
> Many programmers will not have the time to keep up with the sheer
> complexity of C++20. The recent willingness of the C++ standard to
> break previously working code is also unfortunate: another thread has
> revealed something that I didn't know, namely that in C++17, because of
> new lifetime rules, if you have aligned static storage in a buffer of
> type char[] (the type which most aligned uninitialized storage will end
> up being) you have to use std::launder to access objects in the buffer
> through the buffer pointer.
I think this is unnecessary unless the new object's

"
complete object is a const object or it is a base class subobject
"

(see 17.6.4-5) so most code should stay valid (or invalid -- see the discussion
on "Union type punning in C++ redux" thread).

Juha Nieminen

unread,
Feb 24, 2020, 2:50:15 AM2/24/20
to
I don't understand your point.

Or perhaps, it's you who didn't understand my point. I was responding to
"what happened to Stroustrup's request to slow down the development?"
with "it has already been 9 years with almost nothing happening,
how much slower than that does it need to be (if this C++20 is
"too fast")?"

Juha Nieminen

unread,
Feb 24, 2020, 2:52:02 AM2/24/20
to
Lynn McGuire <lynnmc...@gmail.com> wrote:
> Wow, that is a lot of new stuff that I probably will not use.

I'm quite certain that many people said exactly that in 2011, and who are
now using quite many C++11 features.

Jorgen Grahn

unread,
Feb 24, 2020, 3:20:55 AM2/24/20
to
You may have misunderstood me: I didn't ask for a slowdown;
I literally wondered what effect Stroustrup's complaints had.

Chris Vine

unread,
Feb 24, 2020, 5:53:29 AM2/24/20
to
On Mon, 24 Feb 2020 01:22:22 -0500
Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> Chris Vine wrote:
[snip]
> The recent willingness of the C++ standard to
> > break previously working code is also unfortunate: another thread has
> > revealed something that I didn't know, namely that in C++17, because of
> > new lifetime rules, if you have aligned static storage in a buffer of
> > type char[] (the type which most aligned uninitialized storage will end
> > up being) you have to use std::launder to access objects in the buffer
> > through the buffer pointer.
>
> I think this is unnecessary unless the new object's
>
> "
> complete object is a const object or it is a base class subobject
> "
>
> (see 17.6.4-5) so most code should stay valid (or invalid -- see the
> discussion on "Union type punning in C++ redux" thread).

I doubt you are right. The point is that a char[] (or std::byte[])
buffer is not a "a pointer that pointed to the original object" within
the meaning of §6.8/8 of C++17. At any rate, that is the view of the
authors of cppreference.com, who are usually pretty good. They say
(https://en.cppreference.com/w/cpp/utility/launder) that a typical use
for std::launder is "Obtaining a pointer to an object created by placement
new from a pointer to an object providing storage for that object", and
they give this example:

struct Y {int z;};

alignas(Y) std::byte s[sizeof(Y)];
Y* q = new(&s) Y{2};
const int f = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined behavior:
// reinterpret_cast<Y*>(&s) has value "pointer to s"
// and does not point to a Y object
const int g = q->z; // OK
const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK

The aforementioned code would have been valid in C++98/11/14 in my view.
The new lifetime rules in question seem intended to sanction compiler
optimization as a supplement to the aliasing rules (which are not broken
by the code above), and std::launder forms an optimization barrier for
those cases where this optimization would produce the wrong result.

Chris Vine

unread,
Feb 24, 2020, 6:15:15 AM2/24/20
to
On Mon, 24 Feb 2020 10:53:33 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> The aforementioned code would have been valid in C++98/11/14 in my
> view.

And indeed I see that Stroustrup constructs a similar buffer to
illustrate the alignas declaration at section 6.2.9 of his "The C++
Programming Language", 4th Edition. (He uses std::unitialized_copy()
to put items in the buffer, which is a wrapper for placement new.)

Daniel

unread,
Feb 24, 2020, 8:20:02 AM2/24/20
to
On Monday, February 24, 2020 at 6:15:15 AM UTC-5, Chris Vine wrote:
>
> And indeed I see that Stroustrup constructs a similar buffer to
> illustrate the alignas declaration at section 6.2.9 of his "The C++
> Programming Language", 4th Edition.

boost now uses

template <class T, class U>
T launder_cast(U* u)
{
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
return std::launder(reinterpret_cast<T>(u));
#elif defined(BOOST_GCC) && BOOST_GCC_VERSION > 80000
return __builtin_launder(reinterpret_cast<T>(u));
#else
return reinterpret_cast<T>(u);
#endif
}

internally in a couple of places.

Chris Vine

unread,
Feb 24, 2020, 10:50:41 AM2/24/20
to
Rob Pike is famously supposed to have said something to that effect on
seeing C++11, and in consequence started the Go language at google with
Ken Thompson.

Jorgen Grahn

unread,
Feb 24, 2020, 5:53:28 PM2/24/20
to
There's something wrong with that story: according to Wikipedia, Go
was designed in 2007.

Chris Vine

unread,
Feb 24, 2020, 7:33:57 PM2/24/20
to
On 24 Feb 2020 22:53:17 GMT
Jorgen Grahn <grahn...@snipabacken.se> wrote:
> On Mon, 2020-02-24, Chris Vine wrote:
> > On Mon, 24 Feb 2020 07:51:54 -0000 (UTC)
> > Juha Nieminen <nos...@thanks.invalid> wrote:
> >> Lynn McGuire <lynnmc...@gmail.com> wrote:
> >> > Wow, that is a lot of new stuff that I probably will not use.
> >>
> >> I'm quite certain that many people said exactly that in 2011, and who are
> >> now using quite many C++11 features.
> >
> > Rob Pike is famously supposed to have said something to that effect on
> > seeing C++11, and in consequence started the Go language at google with
> > Ken Thompson.
>
> There's something wrong with that story: according to Wikipedia, Go
> was designed in 2007.

Here's a varbatim extract from what Rob Pike said about it in a talk in
2012 (text licensed by him under the Creative Commons Attribution 3.0
License). Please don't argue with me about whether he is right or not:
take it up with him if you feel strongly about it. (I happen to think
C++11 was pretty good.)

Back around September 2007, I was doing some minor but central work
on an enormous Google C++ program, one you've all interacted with,
and my compilations were taking about 45 minutes on our huge
distributed compile cluster. An announcement came around that there
was going to be a talk presented by a couple of Google employees
serving on the C++ standards committee. They were going to tell us
what was coming in C++0x, as it was called at the time. (It's now
known as C++11).

In the span of an hour at that talk we heard about something like 35
new features that were being planned. In fact there were many more,
but only 35 were described in the talk. Some of the features were
minor, of course, but the ones in the talk were at least significant
enough to call out. Some were very subtle and hard to understand, like
rvalue references, while others are especially C++-like, such as
variadic templates, and some others are just crazy, like user-defined
literals.

At this point I asked myself a question: Did the C++ committee really
believe that was wrong with C++ was that it didn't have enough
features?

***

But the C++0x talk got me thinking again. One thing that really
bothered me—and I think Ken and Robert as well—was the new C++ memory
model with atomic types. It just felt wrong to put such a
microscopically-defined set of details into an already over-burdened
type system. It also seemed short-sighted, since it's likely that
hardware will change significantly in the next decade and it would be
unwise to couple the language too tightly to today's hardware.

We returned to our offices after the talk. I started another
compilation, turned my chair around to face Robert, and started asking
pointed questions. Before the compilation was done, we'd roped Ken in
and had decided to do something. We did not want to be writing in C++
forever, and we—me especially—wanted to have concurrency at my
fingertips when writing Google code. We also wanted to address the
problem of "programming in the large" head on, about which more later.

Pavel

unread,
Feb 25, 2020, 2:00:54 AM2/25/20
to
IMHO it is UB type punning either way (i.e. with or without std::launder) (I
know that the code is from cppreference). Please see the whole argument in
"Union type punning in C++ redux thread". Regardless of whether you agree to my
position in there, I think in case of std::launder, the Standard is quite clear
(for a change) so citing the complete subsection (by n4849):

"
17.6.4 Pointer optimization barrier [ptr.launder]
template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;

1 Mandates: !is_function_v<T> && !is_void_v<T> is true.

2 Preconditions: p represents the address A of a byte in memory. An object X
that is within its lifetime (6.7.3) and whose type is similar (7.3.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).

3 Returns: A value of type T* that points to X.

4 Remarks: An invocation of this function may be used in a core constant
expression whenever the value of its argument may be used in a core constant
expression. A byte of storage is reachable through a pointer value that points
to an object Y if it is within the storage occupied by Y, an object that is
pointer-interconvertible with Y, or the immediately-enclosing array object if Y
is an array element.

5 [Note: If a new object is created in storage occupied by an existing object of
the same type, a pointer to the original object can be used to refer to the new
object unless its complete object is a const object or it is a base class
subobject; in the latter cases, this function can be used to obtain a usable
pointer to the new object. See 6.7.3. — end note]

6 [Example:
struct X { int n; };
const X *p = new const X{3};
const int a = p->n;
new (const_cast<X*>(p)) const X{5}; // p does not point to new object (6.7.3)
// because its type is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK
— end example]
"

Please note:

a) the name of the section, std::launder is a [compiler -- IMHO] *optimization
barrier*.

b) that to call std::launder the type of the object currently in memory shall be
"similar" to the type argument of std::launder template and hence to the type to
which p points. Note that how exactly the p is received does not matter (so the
type of the object from which it was reinterpret_cast does not matter) -- it is
exactly the point of std::launder to "build" (see below) from the pointer to the
old object a pointer to the new object (of _similar type_ and through which all
bytes of the storage occupied by the new object shall be _reachable_) occupying
the same memory that the old object occupied before.

c) the [Note:]. In Library descriptions, Notes are informative rather than
normative so my guess it is here exactly and only to answer "why" question. The
Note claims that, in general the pointer to the old object can be simply used to
access the new object -- "unless its complete object is a const object or it is
a base class subobject" -- as I briefly cited above -- which case is exactly the
reason for launder to be called. [Note] hints at kinds of optimizations the
"Pointer optimization barrier" intends to prohibit: in particular I would
speculate that it needs to prohibit at least optimizing out reads via the old
pointer-to-const or pointer-to-base subobject that compiler otherwise could
replace with e.g. the values earlier loaded via the old pointer to registers
(the former -- by reasoning that the const object shall not have changed; and
the latter -- because the destruction and recreation of a derived object could
be not traceable or not required by the Standard to be traced by the compiler if
it can only see the code operating with a pointer to a base subobject of that
derived object).

d) finally, that the [Example] in the Standard is in direct and precise
correspondence with the [Note] -- as opposed to the example at cppreference.
Specifically, the [Example] does not use reinterpret_cast or static_cast
distractions but instead demonstrates how the pointer to the destroyed const
object can be used to build a pointer through which an object created later in
the same storage ("new object") could be safely accessed. (At runtime, such
"build" itself is probably no-op and the new pointer can very well have same
value and even occupy same memory location or a register as the old pointer --
but at compile time the compiler would know to build the code that actually
accesses the storage for new accesses via so built new pointer -- instead of
using the values read earlier via the old pointer and cached somewhere. In other
words, std::launder seems to be an explicit anti-aliasing of sort for similar
types).

Jorgen Grahn

unread,
Feb 25, 2020, 2:30:21 AM2/25/20
to
On Tue, 2020-02-25, Chris Vine wrote:
> On 24 Feb 2020 22:53:17 GMT
> Jorgen Grahn <grahn...@snipabacken.se> wrote:
>> On Mon, 2020-02-24, Chris Vine wrote:
>> > On Mon, 24 Feb 2020 07:51:54 -0000 (UTC)
>> > Juha Nieminen <nos...@thanks.invalid> wrote:
>> >> Lynn McGuire <lynnmc...@gmail.com> wrote:
>> >> > Wow, that is a lot of new stuff that I probably will not use.
>> >>
>> >> I'm quite certain that many people said exactly that in 2011, and who are
>> >> now using quite many C++11 features.
>> >
>> > Rob Pike is famously supposed to have said something to that effect on
>> > seeing C++11, and in consequence started the Go language at google with
>> > Ken Thompson.
>>
>> There's something wrong with that story: according to Wikipedia, Go
>> was designed in 2007.
>
> Here's a varbatim extract from what Rob Pike said about it in a talk in
> 2012 (text licensed by him under the Creative Commons Attribution 3.0
> License).

[snip quote]

Thanks! It made more sense in his words.

> Please don't argue with me about whether he is right or not: take it
> up with him if you feel strongly about it. (I happen to think C++11
> was pretty good.)

So do I -- to me, it's mostly a polished C++98, supporting the same
things that C++98 did, only better. Although unlike Pike I don't care
about concurrency ...

I complained mostly because I didn't think work on (what became) C++11
had started in 2007, but thinking again of the C++0x name, of course
it had.

Chris Vine

unread,
Feb 25, 2020, 6:01:40 AM2/25/20
to
On 25 Feb 2020 07:30:10 GMT
Jorgen Grahn <grahn...@snipabacken.se> wrote:
> On Tue, 2020-02-25, Chris Vine wrote:
[snip]
> > (I happen to think C++11 was pretty good.)
>
> So do I -- to me, it's mostly a polished C++98, supporting the same
> things that C++98 did, only better. Although unlike Pike I don't care
> about concurrency ...

While I think that C++11 was pretty good - rvalue references and
variadic templates were like a breath of fresh air - and I can see the
point of concepts and modules in C++20, it seems to me that C++ is
becoming so complex that it is beyond the ability of the normal
programmer to come to grips with all of it. This results in bugs. C++
has grown as a loosely pinned set of features, not all of which are
consistent and which are a mess of corner cases where one edge meets
another. Some features look as if they were championed by dilletantes,
not by practising programmers. It lacks regularity and consistency.
C++ is not alone in complexity: Haskell for example is also complex and
difficult to grasp (although it is largely regular), but Haskell is not
a mainstream programming language like C++ is supposed to be.

This doesn't worry me especially. I use other languages also (not yet
rust), including functional languages, and enjoy them. However I do get
annoyed by gratuitous code-breaking things in C++, like needing to apply
std::launder to buffers updated with placement new. Surely we must all
have written circular buffers which construct elements in place with
placement new? What's the point with std::launder for heaven's sake -
the compiler can see you have used placement new, that necessarily
implies that the memory location concerned has been mutated, so when
code accesses the buffer with the correct cast to meet aliasing rules,
make sure the dereferenced values supplied are the correct ones. You
shouldn't need to burden the programmer with having to remember to use
std::launder to get the correct code generated.

I am not convinced even the experts, such as those at cppreference.com
or those on the standard committee, understand the rules on std::launder
and placement new. I notice that C++20 has now rowed back from C++17 on
some of this: in reponse to protestations, you no longer need to use
std::launder to access an object constructed by placement new by reason
of it having a const or reference member. However, nothing has changed
on using std::launder with aligned char[]/std::byte[] buffers. I am
still not 100% sure std::launder is required for this and that
cppreference.com is right on the point, but nor does anyone else seem to
be.

Chris Vine

unread,
Feb 25, 2020, 7:13:31 AM2/25/20
to
On Tue, 25 Feb 2020 02:00:42 -0500
I completely disagree with you that leaving aside std::launder it is UB,
so that does somewhat colour my view on the remainder of your views. It
seems to me that you are just plucking this out of the air. Leaving
aside std::launder, this is very well established code. If it didn't
work there would be no point in having placement new (you couldn't use
it in uninitialized memory returned by std::malloc() or operator new()
either). Something similar appears in Stroustrup's TC++PL.

> I think in case of std::launder, the Standard is quite clear
> (for a change) so citing the complete subsection (by n4849):
>
> 17.6.4 Pointer optimization barrier [ptr.launder]
[snip]

None of this deals with the issue now in question. §21.6.4 of C++17
is concerned with the preconditions to using std::launder (which would
be met here) and the effects thereof, but not with the circumstances in
which §6.8/8 of C++17 requires the use of std::launder. In the case
cited above, on the face of it there are a number of questions going to
the extent to which §6.8/8 of C++17 is relevant to begin with:

i. By virtue of the implicit decay of arrays to pointers, is the array
name, when combined with the pointer arithmetic used in accessing array
members, a "pointer that pointed to the original object".

ii. If so, does it cease to be so if (as above) the type of the buffer
element type (std::byte) is different from the type constructed in it
by placement new (type Y)?

iii. Does §6.8/8 have any relevance where, as in the case cited above,
placement new is used to construct an object in unninitialized memory
for the first time? (On the face of it §6.8/8 is concerned with
constructing a replacement object in initialized memory after the
lifetime of the previous object has ended).

Juha Nieminen

unread,
Feb 25, 2020, 8:52:06 AM2/25/20
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> it seems to me that C++ is
> becoming so complex that it is beyond the ability of the normal
> programmer to come to grips with all of it.

This is something that has been being said, non-stop, for the past 20 years.

Chris Vine

unread,
Feb 25, 2020, 8:55:41 AM2/25/20
to
On Tue, 25 Feb 2020 12:13:36 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> I completely disagree with you that leaving aside std::launder it is UB,
> so that does somewhat colour my view on the remainder of your views. It
> seems to me that you are just plucking this out of the air. Leaving
> aside std::launder, this is very well established code. If it didn't
> work there would be no point in having placement new (you couldn't use
> it in uninitialized memory returned by std::malloc() or operator new()
> either). Something similar appears in Stroustrup's TC++PL.

I see it is also specifically permitted by §4.5/3 of C++17:

If a complete object is created (8.3.4) in storage associated with
another object e of type "array of N unsigned char" or of type "array
of N std::byte" (21.2.1), 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 smaller array object that satisfies these constraints.

The way you create an object in storage associated with another object
of array type is with placement new.

Melzzzzz

unread,
Feb 25, 2020, 8:57:11 AM2/25/20
to
Yeah, and response is very few use all of it :)

--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Svi smo svedoci - oko 3 godine intenzivne propagande je dovoljno da jedan narod poludi -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

Chris Vine

unread,
Feb 25, 2020, 9:40:20 AM2/25/20
to
And the relative use of C++ in projects has dropped in the past 20
years also. Whether that's because people are put off by C++'s
complexity and proneness to having undefined behavior spring out to get
you, or because they think other languages are better in other ways, is
difficult to say. Probably factors such as garbage collection,
developer support and type safety are also relevant.

Pavel

unread,
Feb 25, 2020, 11:08:37 PM2/25/20
to
I think we are fully on the same page about using placement new.

All I am saying is that I do not see how the Standard allows using
reinterpret_cast (in this case, from the pointer to e or its first element) to
obtain the pointer to that complete object (created by the placement new).

Melzzzzz

unread,
Feb 26, 2020, 12:03:19 AM2/26/20
to
Without that you couldn't implement allocator in C++...

Pavel

unread,
Feb 26, 2020, 12:55:09 AM2/26/20
to
I am not saying it does not work. I am saying the behavior of this code is not
specified by the Standard. I am not alone at this either. If you can deduce from
the Standard that this code is well defined, I am very eager to know your deduction.

> there would be no point in having placement new (you couldn't use
> it in uninitialized memory returned by std::malloc() or operator new()
> either).
Why? You can use it in at least the following ways:

1. Placement operator new, as any other, returns a pointer to the object created
(see 7.6.2.7-5) -- you can use the return value.
2. Placement operator new can be used as in the example from the Standard for
launder I cited -- to create an object in a space occupied by an object of a
similar type to which you already hold a pointer. Then you can continue using
that pointer to access the new object, often directly and sometimes you will
have to use it via std::launder (under the conditions well described by the
Standard).
3. Placement operator new can be used to initialize a member (including that of
a union) or an array element of similar type and that member can then be accessed.

There may be other well-defined uses, too -- I am not claiming that the above
list is exhaustive.

> Something similar appears in Stroustrup's TC++PL.
>
>> I think in case of std::launder, the Standard is quite clear
>> (for a change) so citing the complete subsection (by n4849):
>>
>> 17.6.4 Pointer optimization barrier [ptr.launder]
> [snip]
>
> None of this deals with the issue now in question. §21.6.4 of C++17
> is concerned with the preconditions to using std::launder (which would
> be met here) and the effects thereof, but not with the circumstances in
> which §6.8/8 of C++17 requires the use of std::launder.

I disagree. I apologize, I do not have the 2017 standard, only C++20 draft n4849
so my numeration is different from yours. I completely cited the description I
have in the previous post. There the preconditions are specified in a
(normative) paragraphs 17.6.4-1 and 2 and the explanation on why you might use
it is in (non-normative) Note in 17.6.4-5. The (non-normative) example 17.6.4-6
fully agrees with the text of the Note.

I think that I correctly match the requirements you refer to paragraph 8 of
6.7.3 Lifetime in n4849., namely these 4 conditions (8.1 - 8.4) that I think are
well covered by the example from the launder spec I copied to my previous post;
also the example below those conditions seems to confirm my conclusions):

8 If, after the lifetime of an object has ended and before the storage which the
object occupied is reused or
released, a new object is created at the storage location which the original
object occupied, a pointer that
pointed to the original object, a reference that referred to the original
object, or the name of the original
object will automatically refer to the new object and, once the lifetime of the
new object has started, can be
used to manipulate the new object, if:
(8.1) — the storage for the new object exactly overlays the storage location
which the original object occupied,
and
(8.2) — the new object is of the same type as the original object (ignoring the
top-level cv-qualifiers), and
(8.3) — the original object is neither a complete object that is const-qualified
nor a subobject of such an object,
and
(8.4) — neither the original object nor the new object is a
potentially-overlapping subobject (6.7.2).

[Example:

struct C {
int i;
void f();
const C& operator=( const C& );
};
const C& C::operator=( const C& other) {
if ( this != &other ) {
this-> ~ C(); // lifetime of *this ends
new (this) C(other); // new object of type C created
f(); // well-defined
}
return *this;
}

C c1;
C c2;
c1 = c2; // well-defined
c1.f(); // well-defined; c1 refers to a new object of type C


— end example]


please note that launder is nowhere to be found and the comments say that the
code using new object is well-defined. All I am trying to say that the
conditions for the pointer to the old object to be valid for accessing the new
object are quite broad and the launder was introduced to allow compiler
optimizations in cases when it cannot easily determine that the object may have
been replaced.

> In the case
> cited above, on the face of it there are a number of questions going to
> the extent to which §6.8/8 of C++17 is relevant to begin with:
>
> i. By virtue of the implicit decay of arrays to pointers, is the array
> name, when combined with the pointer arithmetic used in accessing array
> members, a "pointer that pointed to the original object".
I believe so.
>
> ii. If so, does it cease to be so if (as above) the type of the buffer
> element type (std::byte) is different from the type constructed in it
> by placement new (type Y)?
I believe so. After the new object of type Y is created, the old object (e.g an
array of bytes is gone. The pointer to the old object, however, still represents
"the address of the storage location" that the old object occupied -- until the
storage is released and can be used in limited ways as explained in paragraph 6
ibid (e.g. "using the pointer as if the pointer were of type void* is
well-defined").
>
> iii. Does §6.8/8 have any relevance where, as in the case cited above,
> placement new is used to construct an object in unninitialized memory
> for the first time? (On the face of it §6.8/8 is concerned with
> constructing a replacement object in initialized memory after the
> lifetime of the previous object has ended).

I think you are correct. The whole purpose of paragraph 8 seems to be to define
when one can use a pointer or reference to or the name of the original object --
and there is no original object if the memory is uninitialized. I think the
paragraph 6 specifies what can be done with / is guaranteed for the pointer to
uninitialized memory.


Juha Nieminen

unread,
Feb 26, 2020, 3:18:27 AM2/26/20
to
In my own professional experience demand for modern C++ expertise has been
raising. This is particularly, and curiously, so especially in embedded
development, of all things.

Maciej Sobczak

unread,
Feb 26, 2020, 3:56:35 AM2/26/20
to
> And the relative use of C++ in projects has dropped in the past 20
> years also.

The interesting part in this observation is the word "relative".
But do you have any data (I don't) on *absolute* numbers?
Because the fact that other technology niches emerged in the mean time, with their own technologies, does not necessary mean that the demand for C++ diminishes.
It might also be the case that infrastructure development (which is a natural domain of C++) is concentrating in the hands of few big companies. So that C++ becomes a technology for Googles and Facebooks, but not necessarily for the average man-and-his-dog startup. So the social coverage of C++ my be changing, but not necessarily the total numbers.
Of course, it's difficult to get the actual data.

> Whether that's because people are put off by C++'s
> complexity and proneness to having undefined behavior spring out to get
> you, or because they think other languages are better in other ways, is
> difficult to say. Probably factors such as garbage collection,
> developer support and type safety are also relevant.

My impression is that on a mass scale, such technical issues are not decision factors. People are writing Android apps in Java (or whatever they are writing in this week) not because of their careful and well-informed considerations of UB and GC, but because this is what this whole ecosystem is pushing on them. They didn't choose Java as a language, but they chose Android as a target.

I agree with Juha's other comment about the curiously rising presence of C++ in the embedded domain. At least all hardware vendors provide C++-capable development environments to enable such mainstream adoption and with the exploding functionality of embedded devices (everything is IoT, AI, and whatnot) this might be just unavoidable. Which is certainly good for the C++ community.

--
Maciej Sobczak * http://www.inspirel.com

Jorgen Grahn

unread,
Feb 26, 2020, 4:14:28 AM2/26/20
to
On Wed, 2020-02-26, Maciej Sobczak wrote:
...

> I agree with Juha's other comment about the curiously rising
> presence of C++ in the embedded domain. At least all hardware
> vendors provide C++-capable development environments to enable such
> mainstream adoption and with the exploding functionality of embedded
> devices (everything is IoT, AI, and whatnot) this might be just
> unavoidable. Which is certainly good for the C++ community.

Yes. I don't see what's curious about it though: C++ is a drop-in
replacement for C from a runtime perspective.

Öö Tiib

unread,
Feb 26, 2020, 4:47:31 AM2/26/20
to
On Wednesday, 26 February 2020 10:18:27 UTC+2, Juha Nieminen wrote:
> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> > On Tue, 25 Feb 2020 13:51:55 -0000 (UTC)
> > Juha Nieminen <nos...@thanks.invalid> wrote:
> >> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> >> > it seems to me that C++ is
> >> > becoming so complex that it is beyond the ability of the normal
> >> > programmer to come to grips with all of it.
> >>
> >> This is something that has been being said, non-stop, for the past 20 years.
> >
> > And the relative use of C++ in projects has dropped in the past 20
> > years also. Whether that's because people are put off by C++'s
> > complexity and proneness to having undefined behavior spring out to get
> > you, or because they think other languages are better in other ways, is
> > difficult to say. Probably factors such as garbage collection,
> > developer support and type safety are also relevant.
>
> In my own professional experience demand for modern C++ expertise has been
> raising. This is particularly, and curiously, so especially in embedded
> development, of all things.

I have observed same thing. Hiring C++ programmers has became
hard. Can be that the schools prepare less of them.
Can be that C++ has became too complex to learn on your own.
Can be tendency of equipment manufacturers to make IOT
(or outright web servers) into their embedded equipment while
being resistant to increase bills of materials.
Can be raise in usage of hand-held stuff and battery life of such.
Can be the hipster culture of people wanting to have
special, customized precisely matching their personality.
Can be more effort needed against platform providers artifically
causing issues against usage of C++. Can be that the modern
snow-flake people just are useless as engineers.
Whatever it is, hiring at least half-decent C++ programmers has
became all harder and harder.

Chris Vine

unread,
Feb 26, 2020, 7:57:37 AM2/26/20
to
I do not see how you think the standard does not allow using
reinterpret_cast in this case.

The standard provides that an object pointer can be converted to an
object pointer of a different type using reinterpret_cast. Leaving
aside lifetime issues involving std::launder, the problem with doing so
is two-fold. First it might result in incorrect alignment. Secondly it
might break C++'s aliasing rules by type punning. Neither occurs here.
alignas is used so alignment is correct. And there is no type punning:
there is an object of type Y (as created by placement new) at the
address in question. There would be type punning if you tried to use
the std::byte array to access the Y object without a reinterpret_cast,
say to examine individual bytes of the object, although as it happens
that is type punning which is blessed by the standard - §6.10/8.8 of
C++17.

You seem to have something wrong with your mental picture of this.

Pavel

unread,
Feb 27, 2020, 12:24:34 AM2/27/20
to
I think gcc-9.2 C++ Standard Library implements std::allocator without using
reinterpret_cast (some other non-standard allocators provided with the library
do use it but that does not invalidate the point).

Of course above I assume that the allocation functions "... void* operator new
..." are not parts of std::allocator implementation but underlying low-level
implementation details.

On a side note, it is telling that reinterpret_cast is not used in this
implementation even though it is a special library packed with the compiler and
as such it is the one library that _could_ use the compiler's
implementation-specific behavior (including that of reinterpret_cast) without
running afoul of the Standard.

>>
>
>

Pavel

unread,
Feb 27, 2020, 12:50:43 AM2/27/20
to
Öö Tiib wrote:
> On Wednesday, 26 February 2020 10:18:27 UTC+2, Juha Nieminen wrote:
>> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
>>> On Tue, 25 Feb 2020 13:51:55 -0000 (UTC)
>>> Juha Nieminen <nos...@thanks.invalid> wrote:
>>>> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
>>>>> it seems to me that C++ is
>>>>> becoming so complex that it is beyond the ability of the normal
>>>>> programmer to come to grips with all of it.
>>>>
>>>> This is something that has been being said, non-stop, for the past 20 years.
>>>
>>> And the relative use of C++ in projects has dropped in the past 20
>>> years also. Whether that's because people are put off by C++'s
>>> complexity and proneness to having undefined behavior spring out to get
>>> you, or because they think other languages are better in other ways, is
>>> difficult to say. Probably factors such as garbage collection,
>>> developer support and type safety are also relevant.
>>
>> In my own professional experience demand for modern C++ expertise has been
>> raising. This is particularly, and curiously, so especially in embedded
>> development, of all things.
>
> I have observed same thing. Hiring C++ programmers has became
> hard.
Hiring a COBOL or FORTRAN-66 programmer could be even harder :-). Just kidding.

> Can be that the schools prepare less of them.
> Can be that C++ has became too complex to learn on your own.
> Can be tendency of equipment manufacturers to make IOT
> (or outright web servers) into their embedded equipment while
> being resistant to increase bills of materials.
> Can be raise in usage of hand-held stuff and battery life of such.
I think unexpectedly relevant to this is David Gross's talk on C++
micro-benchmarking at https://www.youtube.com/watch?v=Czr5dBfs72U.

Hopefully listening to the talk will be worth it for some people here for its
main subject; but it is also preceded with a number of reasons for why we may
want to write in C++ with one being saving the world the from climate change.

I found it ironic but also convincing (of course it is easy to get convinced
that you are saving the world so I might be biased here):

In a typical infrastructure of a tech firm running few hundreds or thousands
power-hungry UNIX servers replacing Java (not to mention Python and such) with
decently written C++ code can easily make possible to retire two-third of them.

Maciej Sobczak

unread,
Feb 27, 2020, 2:56:05 AM2/27/20
to
> I don't see what's curious about it though: C++ is a drop-in
> replacement for C from a runtime perspective.

Unless you have messed up your run-time so much (or you have so little of it), that the replacement is not possible at all.

This is usually not a concern on desktop or on server, where developers treat the runtime as a black-box part of the development environment, but occasionally happens in the world of embedded systems, where engineers are more keen hacking their own linker scripts or startup sequences. If you mess it up to the extent that nobody wants to support it any longer, then introducing the programming language that is much more demanding with its constructors of static objects, virtual function tables, exceptions and what not, might be a challenge that nobody will want to take. I can imagine teams getting stuck like this forever.

But fortunately, hardware vendors provide complete IDEs that handle both C and C++ reasonably well, so this should not be a concern for new projects. As a personal story, I have recently managed to port a mature high-level C++ messaging library to several embedded platforms without any issues caused by the language, the only challenge being the peculiarities of the target real-time operating systems and their network stacks. This positive porting experience allows me to treat C++ as a perfectly validated solution for embedded systems and I expect to see more of it in this space.

Chris Vine

unread,
Feb 27, 2020, 6:21:37 AM2/27/20
to
You keep going on about reinterpret_cast. To remind you, the code in
question which you previously said had undefined behaviour, and you now
seem to say has implementation defined behaviour, is this:

struct Y {int z;};
alignas(Y) std::byte s[sizeof(Y)];
Y* q = new(&s) Y{2};
const int h = std::launder(reinterpret_cast<Y*>(&s))->z;

So just what behaviour do you think is implementation-defined here?
Some uses of reinterpret_cast can have implementation defined behaviour
(the mapping of pointer to integer, and the effect of conversions
between object pointer and function pointer) but these are not relevant
here.

Just repeating yourself does not make what you say true. Let's have a
proper explanation, preferably with reference to some authoritative
work[1].

Chris

[1] There are some issues to consider in addition to aliasing and
alignment previously mentioned (for example, what address does the
global placement new operator return by reference to the address of its
void* argument). Unfortunately you are on the wrong side of that one
also.

Pavel

unread,
Feb 28, 2020, 12:42:35 AM2/28/20
to
Please do not put your words in my mouth. I am saying that it is ok for C++
Standard Library "to use the compiler implementation-specific behavior"
(removing possessive case but all same words).

From the below definition of undefined behavior:

3.28 [defns.undefined]
undefined behavior
behavior for which this document imposes no requirements
[Note 1 to entry: Undefined behavior may be expected when this document omits
any explicit definition of
behavior or when a program uses an erroneous construct or erroneous data.
Permissible undefined behavior
ranges from ignoring the situation completely with unpredictable results, *to
behaving during translation or
program execution in a documented manner characteristic of the environment*
(with or without the issuance
of a diagnostic message), to terminating a translation or execution (with the
issuance of a diagnostic message).
Many erroneous program constructs do not engender undefined behavior; they are
required to be diagnosed.
Evaluation of a constant expression never exhibits behavior explicitly specified
as undefined in Clause 4
through Clause 15 of this document (7.7). — end note]

the compiler implementation-specific behavior means exactly that compiler
behaves "during translation or program execution in a documented manner
characteristic of the environment". As long as no requirements on this behavior
is imposed by the Standard ("this document"), it is also undefined behavior
simply by definition.


> is this:
>
> struct Y {int z;};
> alignas(Y) std::byte s[sizeof(Y)];
> Y* q = new(&s) Y{2};
> const int h = std::launder(reinterpret_cast<Y*>(&s))->z;
>
> So just what behaviour do you think is implementation-defined here?
Where did I say that "here" had implementation-defined behavior (for the record,
I think it may or may not have that, depending on a specific implementation)? I
did say that [the reinterpret_cast expression above] had undefined behavior
because I cannot see where the Standard imposes any requirements on that
expression (in plain English, it never tells me what the result of this
expression shall be). Hence I have no other choice than to conclude it has
undefined behavior by definition (and believe me I am not enjoying it a little
bit: I have tons of code using reinterpret_cast in similar manner -- although
some of it is protected by compiler-specific #ifdefs but mainly for different
reasons).

> Some uses of reinterpret_cast can have implementation defined behaviour
> (the mapping of pointer to integer, and the effect of conversions
> between object pointer and function pointer) but these are not relevant
> here.
That was exactly my point: even if reinterpret_cast of some mappings is defined
by a _particular_ compiler and therefore the C++ Standard Library could use it
in its implementation of std::allocator -- which gcc-9.2 Library does not do --
the reinterpret_cast behavior for the above code snippet would still be
undefined -- again, regardless of whether some particular implementation
documents it.

>
> Just repeating yourself does not make what you say true.
Exactly. In particular, repeating that I am wrong does not make me wrong.

> Let's have a
> proper explanation, preferably with reference to some authoritative
> work[1].
If the text of the latest Draft n4849 is not authoritative enough I am sorry, I
do not have the official text of C++17. Although, reinterpret_cast above would
be UB even according to C++11 -- whose official text I do have if that satisfies
you. Or do you believe there is a higher authority on the Standard then the
Standard itself?

>
> Chris
>
> [1] There are some issues to consider in addition to aliasing and
> alignment previously mentioned (for example, what address does the
> global placement new operator return by reference to the address of its
> void* argument). Unfortunately you are on the wrong side of that one
> also.
Not again, please. I thought you wanted to give a "proper explanation,
preferably with reference to some authoritative work"? Also, my being wrong
would not be unfortunate? I don't mind standing corrected, it just has not
happened so far in this particular discussion.

-Pavel

Chris Vine

unread,
Feb 28, 2020, 9:44:01 AM2/28/20
to
On Fri, 28 Feb 2020 00:42:15 -0500
Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> Chris Vine wrote:
[snip]
> > You keep going on about reinterpret_cast. To remind you, the code in
> > question which you previously said had undefined behaviour, and you now
> > seem to say has implementation defined behaviour,
> > is this:
> >
> > struct Y {int z;};
> > alignas(Y) std::byte s[sizeof(Y)];
> > Y* q = new(&s) Y{2};
> > const int h = std::launder(reinterpret_cast<Y*>(&s))->z;
> >
> > So just what behaviour do you think is implementation-defined here?
>
> Where did I say that "here" had implementation-defined behavior (for the record,
> I think it may or may not have that, depending on a specific implementation)? I
> did say that [the reinterpret_cast expression above] had undefined behavior
> because I cannot see where the Standard imposes any requirements on that
> expression (in plain English, it never tells me what the result of this
> expression shall be). Hence I have no other choice than to conclude it has
> undefined behavior by definition (and believe me I am not enjoying it a little
> bit: I have tons of code using reinterpret_cast in similar manner -- although
> some of it is protected by compiler-specific #ifdefs but mainly for different
> reasons).

This helpfully identifies the source of your thinking. I am somewhat
reluctant to continue this because I think from our previous exchanges
it is likely to be fruitless, but here are the main provisions of the
C++17 standard which define the behaviour of this code. I would have
hoped that it would have been apparent from my earlier posts, but here
it is in extenso:

§4.5/3 (a complete object may be constructed in storage comprising
an array of unsigned char or of std::byte)

§6.8/10 (an object may be accessed through a glvalue of, and
therefore aliased by a pointer to, its dynamic type or amongst other
things a char, unsigned char or std::byte type)

§8.2.9/1 (static_cast<T>(v) converts the expression v to type T,
where it is one of the cases covered by §8.2.9)

§8.2.9/13 (the expression static_cast<T*>(static_cast<void*>(p1))
returns the pointer value unchanged (except as to type) where, as
in our case, the type of p1 and T* are not pointer-interconvertible,
provided that the alignment requirement of type T is met).

§8.2.10/7 (an object pointer can be converted by reinterpret_cast to
an object pointer of a different type: reinterpret_cast<cv T*>(v) is
equivalent to static_cast<cv T*>(static_cast<cv void*>(v)))

§8.3.6/1 ((alignof T) yields the alignment requirement of type T)

§10.6.2/3 (alignas(T) is the same as (alignas (alignof T)), and
results in its operand meeting the alignment requirement of T)

§21.6.2.3/1 (the return value of the placement new operator for single
objects is its placement pointer value unchanged, and that operator
performs no other action than return it)

§21.6.4 (std::launder permits access to an object that is within its
lifetime and whose storage is located at the address given by its
operand - say, in an array of unsigned char or std::byte, as the
corollary of §4.5/3).

You will I suspect be inclined to dismiss all this. If so, can I
invite you to consider three additional points. First, as I have
previously pointed out, the authors of cppreference.com think the code
is correct and I suspect they know more about it than you do.
Secondly, as I have also pointed out, Stroustrup's TC++PL contains a
similar example (using std::uninitialized_copy()) and I suspect he
knows more about it than you do.

Thirdly you might then respond "that's an argument ad verecundiam", and
indeed it is, although I have also given you the argument on the facts.
Let me alternatively appeal to common sense and give an example of a
case where you cannot feasibly directly use the return value of
placement new (as opposed to the buffer value passed to it) in the way
you have previously suggested. This is the very common idiom of using
an array of unsigned char or std::byte to construct an uninitialized
buffer for objects of class type T, let's say to make a fixed-size
circular buffer of up to 100 T elements. For circular buffers you
would commonly construct an element in the buffer using placement new,
and then, when you subsequently copy/move the element from the buffer,
destroy the buffer element by calling its destructor explicitly. You
could not however store the pointer value returned by the application of
placement new without keeping a separate array of T* pointers to shadow
each of the possible 100 or less T elements in the array of unsigned
char at any one time. That would be a ridiculous waste of memory and
even if you doubt the competence of cppreference.com and/or Stroustrup,
you must surely think that the authors of the C++ standard would have
taken the effort to cover this, particularly given that the standard
explicitly permits the construction of objects in arrays of unsigned
char or std::byte.

Pavel

unread,
Feb 29, 2020, 7:52:52 PM2/29/20
to
I hope not but it depends on your definition of fruitless.

> but here are the main provisions of the
> C++17 standard which define the behaviour of this code. I would have
> hoped that it would have been apparent from my earlier posts, but here
> it is in extenso:
>
> §4.5/3 (a complete object may be constructed in storage comprising
> an array of unsigned char or of std::byte)
>
> §6.8/10 (an object may be accessed through a glvalue of, and
> therefore aliased by a pointer to, its dynamic type or amongst other
> things a char, unsigned char or std::byte type)
this does not seem relevant as reinterpret_cast does not try to access any
object by glvalue it simply converts that glvalue itself (the pointer); and the
subsequent attempt to access the object is done by a pointer already converted
to the "object dynamic type", so you have nothing to prove here.

(*) Also, just for the record, &s in the above code snippet is *not* a pointer
to an object of type std::byte which you seem to imply -- please also see below.

>
> §8.2.9/1 (static_cast<T>(v) converts the expression v to type T,
> where it is one of the cases covered by §8.2.9)
>
> §8.2.9/13 (the expression static_cast<T*>(static_cast<void*>(p1))
> returns the pointer value unchanged (except as to type) where, as
> in our case, the type of p1 and T* are not pointer-interconvertible,
> provided that the alignment requirement of type T is met).
>
> §8.2.10/7 (an object pointer can be converted by reinterpret_cast to
> an object pointer of a different type: reinterpret_cast<cv T*>(v) is
> equivalent to static_cast<cv T*>(static_cast<cv void*>(v)))
>
> §8.3.6/1 ((alignof T) yields the alignment requirement of type T)
>
> §10.6.2/3 (alignas(T) is the same as (alignas (alignof T)), and
> results in its operand meeting the alignment requirement of T)
>
> §21.6.2.3/1 (the return value of the placement new operator for single
> objects is its placement pointer value unchanged, and that operator
> performs no other action than return it)
>
> §21.6.4 (std::launder permits access to an object that is within its
> lifetime and whose storage is located at the address given by its
> operand - say, in an array of unsigned char or std::byte, as the
> corollary of §4.5/3).
>
> You will I suspect be inclined to dismiss all this.
No. I would rather use it to try to pinpoint the root cause of the disagreement.
I think (please correct me if I am wrong) that you believe that given this code:

(**)
struct T2 { double d = 2.0; }; // line 1
struct alignas(T2) T1 { char ca[8]; }; // line 2
static_assert(sizeof(T1) == sizeof(T2)); // line 3
static_assert(alignof(T1) == alignof(T2)); // line 4
T1* const p1 = new T1; // line 5
p1->~T1(); // line 6
new (p1) T2; // line 7
T2* const p2 = reinterpret_cast<T2*>(p1); // line 8
cout << p2->d * 2.0 << endl; // line 9

and assuming it compiles (i.e. size and alignment are ok; also let's pretend
that launder and aliasing do not exist; we may have separate disagreement about
these but IMHO they are irrelevant here), the Standard guarantees that p2 points
to a [complete] object o2 of class T2 and hence the code "will print 777".

To the contrary, I think that the closest thing to the above that the Standard
guarantees is that the "pointer values" of p1 and p2 are same. This is not
enough and here is the proof: (text below are direct citations, from n4849):

(a) [definition of memory address]:
6.7
Memory and objects
6.7.1
Memory model
1 The fundamental storage unit in the C ++ memory model is the byte. ... The
memory available to a C ++ program consists of one or more sequences of
contiguous bytes. Every byte has a unique address.

...
6.8.2 Compound types:

(b) [definition of pointer value via memory address (a value of a pointer
type])] (continuation of paragraph 3 after (3.4))
3
...
A value of a pointer type that is a pointer to or past the end of an object
represents the address of the first byte in memory (6.7.1) occupied by the
object ...

(c) [definition of when reinterpret_cast can be used to convert pointers to
objects] end-of
6.8.2-4:
If two objects are pointer-interconvertible, then they have the same address,
and it is possible to obtain a pointer to one from a pointer to the other via a
reinterpret_cast (7.6.1.9).

(d) [an example of objects' having same address (hence, by definition, pointers
to these objects have same pointer value) but being not
pointer-interconvertible], ibid
[Note: An array object and its first element are not pointer-interconvertible,
even though they have the same address. — end note]

BTW see (*) above about the &s from ccpreference example under discussion not
being a pointer to std::byte. Only mentioning this here as you seem to imply
(please correct me that I am wrong) that accessing an object via the result of
reinterpret_cast from a pointer to std::byte, char etc. when the object is not
pointer-interconvertible with any of these types is somehow "more legal". (I
happen to think these accesses have undefined behaviors; these types are only
discussed specially because of being minimal units of memory and having minimal
alignment).

You may object that in practice the pointers representing same addresses are
identical and hence differentiating between the pointers of type "pointer to T"
with same "pointer values" and those pointing to same object does not make sense
but consider these 2 counterarguments first:

(e) compiler can produce better (e.g. faster or smaller) code if it does not
need to guarantee any defined behavior to the code that may access objects
through a pointer obtained via reinterpret_cast.

(f) consider CPU architecture (simplified) with:
- 64-bit pointers and 63-bit memory addresses;
- each instruction (OP) consisting of OP code, a register operand and a memory
operand (MEND);
- CPU operating on the objects of types naturally mapping to C++ integral,
pointer and floating point types where U64 maps to uint64_6, MEND to pointer and
DBL to double
- OP codes specifying operation and operand size but not type
- two ALUs, ALU0 handling floating point objects and ALU1 all others
- most significant bit (MSB) of a MEND specifying the ALU where CPU scheduler
should direct the OP for processing; and the 63 LSBs representing the address in
memory.

Now try to mentally run the code snippet (**) on a computer system with the CPU
described above:

(g) assume for the sake of example that the operator new in line 5 returns
0x0000000000000010; (wisely basing the decision to not set MSB on knowing that
the first member of T is an array of char (i.e. not a double or array thereof)
and hence it will likely be sent to ALU0

(h) what should reinterpret_cast in line 8 return? It is clearly more efficient
to do nothing than do something so unless the Standard makes it to return a
pointer that is pointer-interconvertible to a pointer to double (which the
Standard does not), it will return 0x0000000000000010.
NOTE: In optimized code this reinterpret_cast will be a no-op (which is how
reinterpret_cast has always been and how it has been intended to be -- a rare
unity); compiler will simply take note that to get hold of `p2' from this point
in the source code and on it needs to use same register that it already
allocated for `p1' and to which it has already loaded the `p1' (Save a register
-- save the World (C) :-) ).

(i) what effect should line 9 produce? I think it may even print something..
maybe.. unless it crashes before.. but unlikely 4.. but still possible.
"Classic" UB as it is often explained in C++ primers (if explains at all).

(j) You may argue that this is such a weird architecture as in (f) above,
although possible (why not?), is uncommon. I do not think it is weird, but ok
for the sake of the example; but as for the commonality I will refer you to a
very broadly used i8086 in "real CPU mode" where every address in its 1-Mb
address space has a variable number of representations (usually >1 and high)
depending on its value and, furthermore, all representations of an address can
be used for some purposes but only certain -- for other purposes.

> If so, can I
> invite you to consider three additional points. First, as I have
> previously pointed out, the authors of cppreference.com think the code
> is correct and I suspect they know more about it than you do.
> Secondly, as I have also pointed out, Stroustrup's TC++PL contains a
> similar example (using std::uninitialized_copy()) and I suspect he
> knows more about it than you do.
>
> Thirdly you might then respond "that's an argument ad verecundiam",
Yes. People make mistakes. A productive person makes more mistakes than an
average person. Of course s/he also produces many more useful
goods/services/ideas than an average person.

> and
> indeed it is, although I have also given you the argument on the facts.
> Let me alternatively appeal to common sense and give an example of a
> case where you cannot feasibly directly use the return value of
> placement new (as opposed to the buffer value passed to it) in the way
> you have previously suggested. This is the very common idiom of using
> an array of unsigned char or std::byte to construct an uninitialized
> buffer for objects of class type T, let's say to make a fixed-size
> circular buffer of up to 100 T elements. For circular buffers you
> would commonly construct an element in the buffer using placement new,
> and then, when you subsequently copy/move the element from the buffer,
> destroy the buffer element by calling its destructor explicitly. You
> could not however store the pointer value returned by the application of
> placement new without keeping a separate array of T* pointers to shadow
> each of the possible 100 or less T elements in the array of unsigned
> char at any one time. That would be a ridiculous waste of memory and
> even if you doubt the competence of cppreference.com and/or Stroustrup,
> you must surely think that the authors of the C++ standard would have
> taken the effort to cover this, particularly given that the standard
> explicitly permits the construction of objects in arrays of unsigned
> char or std::byte.
>

Let me try address this argument. Its essence is (please correct me if I am
wrong), that it impossible to access the results of placement new without
stashing the returned pointer or using reinterpret_cast somewhere and by that
over-complicating the program. I happen to believe that it is, on the contrary,
possible wherever the desired behavior is actually achievable in C++. I would
not take on proving such a broad statement "in general" but I am willing to
solve a specific puzzle (i.e. refactor given code with reasonable *expected*
behavior to ditch the use of reinterpret_cast on not pointer-interconvertible
objects and (hopefully, maybe) even improve its readability) as long it is short
and to the point and do it without resorting to stashing the result of the
placement new (other than maybe for illustration).

As an example, below is a copy of the code from the thread "Union type punning
in C++ redux" where the OP asked about a variant class code they implemented
with reinterpret_cast as follows:

// original OP's code begins ----------------
#include <string>
#include <new>
#include <algorithm>
#include <cstring>

struct B
{
uint8_t tag;
uint8_t extra;
uint64_t n;

B(uint64_t n, uint8_t extra = 0)
: tag{1}, n(n), extra(extra)
{

}
};

struct C
{
uint8_t tag;
double d;

C(double d)
: tag{2}, d(d)
{

}
};

class V
{
static constexpr size_t data_size = std::max(sizeof(B), sizeof(C));
static constexpr size_t data_align = std::max(alignof(B), alignof(C));

typedef typename std::aligned_storage<data_size, data_align>::type data_t;

data_t data_;

public:
V(uint8_t n)
{
::new(&data_) B(n);
}
V(double d)
{
::new(&data_) B(d);
}
~V()
{
switch (tag())
{
case 1:
reinterpret_cast<const B*>(&data_)->~B();
break;
case 2:
reinterpret_cast<const C*>(&data_)->~C();
break;
default:
break;
}
}
uint8_t tag() const
{
uint8_t t;
std::memcpy(&t, &data_, sizeof(uint8_t));
return t;
}
};
// original OP's code ends ----------------

// the replacement for code of V begins ----------------
// (the rest of the original code can be used "as-is";
// you will notice I changed the type of parameter
// `n' of V2() but that is only because I believed
// it was the typo on behalf of the OP so that change seems
// irrelevant to the topic
class V2 {
union {
B b;
C c;
};
public:
V2(uint64_t n): b(n) { }
V2(double d): c(d) { }
~V2() {
switch (tag()) {
case 1:
b.~B();
break;
case 2:
c.~C();
break;
default:
break;
}
}
uint8_t tag() const { return b.tag; }
};
// replacement for code of V ends ----------------

Pavel

unread,
Feb 29, 2020, 7:56:21 PM2/29/20
to
Pavel wrote:

[snipped]
> (**)
> struct T2 { double d = 2.0; }; // line 1
> struct alignas(T2) T1 { char ca[8]; }; // line 2
> static_assert(sizeof(T1) == sizeof(T2)); // line 3
> static_assert(alignof(T1) == alignof(T2)); // line 4
> T1* const p1 = new T1; // line 5
> p1->~T1(); // line 6
> new (p1) T2; // line 7
> T2* const p2 = reinterpret_cast<T2*>(p1); // line 8
> cout << p2->d * 2.0 << endl; // line 9
>
> and assuming it compiles (i.e. size and alignment are ok; also let's pretend
> that launder and aliasing do not exist; we may have separate disagreement about
> these but IMHO they are irrelevant here), the Standard guarantees that p2 points
> to a [complete] object o2 of class T2 and hence the code "will print 777".
[snipped]
correction, I meant to say the code "will print 4".

Chris Vine

unread,
Mar 1, 2020, 6:05:59 AM3/1/20
to
On Sat, 29 Feb 2020 19:52:18 -0500
Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> To the contrary, I think that the closest thing to the above that the Standard
> guarantees is that the "pointer values" of p1 and p2 are same. This is not
> enough and here is the proof: (text below are direct citations, from n4849):

[snip]

> (c) [definition of when reinterpret_cast can be used to convert pointers to
> objects] end-of
> 6.8.2-4:
> If two objects are pointer-interconvertible, then they have the same address,
> and it is possible to obtain a pointer to one from a pointer to the other via a
> reinterpret_cast (7.6.1.9).

[snip]

> You may object that in practice the pointers representing same addresses are
> identical and hence differentiating between the pointers of type "pointer to T"
> with same "pointer values" and those pointing to same object does not make sense
> but consider these 2 counterarguments first:
>
> (e) compiler can produce better (e.g. faster or smaller) code if it does not
> need to guarantee any defined behavior to the code that may access objects
> through a pointer obtained via reinterpret_cast.

The C++ standard does very definitely guarantee that the pointer values
are the same - §8.2.9/13 of C++17 read with the other provisions to
which I have referred. Were the types to be pointer-interconvertible a
reinterpret_cast would be enough - §8.2.9/13. If they are not, as here,
then you have to use std::launder also (I am now satisfied that
cppreference.com is right about that and my complaints on that were
wrong). §21.6.4 directly covers this case. It prevents the kind of
optimization to which you refer.

In fact, until C++23 comes out, constructing nested objects using
placement new within char arrays constructed on free store and then
accessing them with reinterpret_cast and std::launder is the only
technically correct way you can construct containers which allocate
their memory dynamically, including implementing std::vector:
https://stackoverflow.com/questions/60465235/does-stdunitialized-copy-have-undefined-behavior

I am satisfied that you are wrong and everyone else is right.

Try to trim your posts to a reasonable size.

Chris Vine

unread,
Mar 1, 2020, 6:44:31 AM3/1/20
to
On Sun, 1 Mar 2020 11:06:08 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> The C++ standard does very definitely guarantee that the pointer values
> are the same - §8.2.9/13 of C++17 read with the other provisions to
> which I have referred.

By the way the pointer values to which I was referring were those in
the cppreference.com. Your version has the problem that the type
providing storage is neither the same type as the replacement object nor
an array of unsigned char or std::byte.

Pavel

unread,
Mar 1, 2020, 2:02:15 PM3/1/20
to
I addressed the insignificance of the difference for the matter in hand in my
previous point.

Chris Vine

unread,
Mar 1, 2020, 5:13:23 PM3/1/20
to
I don't think so. Your version has undefined behaviour. Instead of
assigning and dereferencing a pointer value your program could play
Yankee Doodle on your sound card. On the other hand, the
cppreference.com code (constructing the object nested in an array of
std::byte and accessing it through std::launder) has defined behaviour.

Chris Vine

unread,
Mar 1, 2020, 5:26:31 PM3/1/20
to
On Sun, 1 Mar 2020 22:13:32 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> On Sun, 1 Mar 2020 14:02:00 -0500
> Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> > Chris Vine wrote:
> > > On Sun, 1 Mar 2020 11:06:08 +0000
> > > Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> > >> The C++ standard does very definitely guarantee that the pointer values
> > >> are the same - §8.2.9/13 of C++17 read with the other provisions to
> > >> which I have referred.
> > >
> > > By the way the pointer values to which I was referring were those in
> > > the cppreference.com. Your version has the problem that the type
> > > providing storage is neither the same type as the replacement object nor
> > > an array of unsigned char or std::byte.
> >
> > I addressed the insignificance of the difference for the matter in hand in my
> > previous point.
>
> I don't think so. Your version has undefined behaviour. Instead of
> assigning and dereferencing a pointer value your program could play
> Yankee Doodle on your sound card. On the other hand, the
> cppreference.com code (constructing the object nested in an array of
> std::byte and accessing it through std::launder) has defined behaviour.

I should add that I still think that this distinction made in C++17
between (i) pointer-interconvertible types and (ii) types constructed at
the same address with placement new in an array of unsigned char or
std::byte (which is explicitly permitted by the standard), is very
unfortunate - thank you for indirectly drawing my attention to this, as
I was not previously alive to it.

This is because I cannot see why in case (ii), applying
reinterpret_cast is insufficient and you need also to apply
std::launder. It should be obvious to the compiler that placement new
mutates memory and take it from there. But probably there are some
desirable compiler optimizations behind it.

Pavel

unread,
Mar 1, 2020, 6:58:09 PM3/1/20
to
Chris Vine wrote:
> On Sun, 1 Mar 2020 14:02:00 -0500
> Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
>> Chris Vine wrote:
>>> On Sun, 1 Mar 2020 11:06:08 +0000
>>> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
>>>> The C++ standard does very definitely guarantee that the pointer values
>>>> are the same - §8.2.9/13 of C++17 read with the other provisions to
>>>> which I have referred.
>>>
>>> By the way the pointer values to which I was referring were those in
>>> the cppreference.com. Your version has the problem that the type
>>> providing storage is neither the same type as the replacement object nor
>>> an array of unsigned char or std::byte.
>>
>> I addressed the insignificance of the difference for the matter in hand in my
>> previous point.
>
> I don't think so. Your version has undefined behaviour.
It is and that was the point. If the source pointer to an array or unsigned char
or std:byte, it still would have undefined behavior.

Chris Vine

unread,
Mar 1, 2020, 7:44:12 PM3/1/20
to
On Sun, 1 Mar 2020 18:58:00 -0500
Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> Chris Vine wrote:
> > On Sun, 1 Mar 2020 14:02:00 -0500
> > Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
> >> Chris Vine wrote:
> >>> On Sun, 1 Mar 2020 11:06:08 +0000
> >>> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> >>>> The C++ standard does very definitely guarantee that the pointer values
> >>>> are the same - §8.2.9/13 of C++17 read with the other provisions to
> >>>> which I have referred.
> >>>
> >>> By the way the pointer values to which I was referring were those in
> >>> the cppreference.com. Your version has the problem that the type
> >>> providing storage is neither the same type as the replacement object nor
> >>> an array of unsigned char or std::byte.
> >>
> >> I addressed the insignificance of the difference for the matter in hand in my
> >> previous point.
> >
> > I don't think so. Your version has undefined behaviour.
>
> It is and that was the point. If the source pointer to an array or unsigned char
> or std:byte, it still would have undefined behavior.

You are going round in circles. If the source was an array of
unsigned char or std:byte, it would not have undefined behaviour if you
used reinterpret_cast with std::launder. The standard is clear on the
point. You prove nothing by producing code which we both agree has
undefined behaviour.

Pavel

unread,
Mar 1, 2020, 9:11:43 PM3/1/20
to
Chris Vine wrote:
> On Sat, 29 Feb 2020 19:52:18 -0500
> Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
>> To the contrary, I think that the closest thing to the above that the Standard
>> guarantees is that the "pointer values" of p1 and p2 are same. This is not
>> enough and here is the proof: (text below are direct citations, from n4849):
>
> [snip]
>
>> (c) [definition of when reinterpret_cast can be used to convert pointers to
>> objects] end-of
>> 6.8.2-4:
>> If two objects are pointer-interconvertible, then they have the same address,
>> and it is possible to obtain a pointer to one from a pointer to the other via a
>> reinterpret_cast (7.6.1.9).
>
> [snip]
>
>> You may object that in practice the pointers representing same addresses are
>> identical and hence differentiating between the pointers of type "pointer to T"
>> with same "pointer values" and those pointing to same object does not make sense
>> but consider these 2 counterarguments first:
>>
>> (e) compiler can produce better (e.g. faster or smaller) code if it does not
>> need to guarantee any defined behavior to the code that may access objects
>> through a pointer obtained via reinterpret_cast.
>
> The C++ standard does very definitely guarantee that the pointer values
> are the same - §8.2.9/13 of C++17 read with the other provisions to
> which I have referred.
I said this much right in the beginning of the post to which I am responding. I
really hate citing myself and sorry I did not assign a an anchor to that
statement, but now I am fixing that:

"
(k) To the contrary, I think that the closest thing to the above that the
Standard guarantees is that the "pointer values" of p1 and p2 are same.
"

Were the types to be pointer-interconvertible a
> reinterpret_cast would be enough - §8.2.9/13. If they are not, as here,
> then you have to use std::launder also (I am now satisfied that
> cppreference.com is right about that and my complaints on that were
> wrong). §21.6.4 directly covers this case. It prevents the kind of
> optimization to which you refer.
You know, that is actually an interesting argument to which I almost agreed. To
be intellectually honest and maybe convince you to change your mind I have to
address it at length.

std::launder definition uses a term that is not explicitly defined by the
Standard, namely "a pointer value that points to an object".

This term seems to be used a couple of times in the Standard in a sense "a
pointer that represents the address of a storage location where the object is
located" as opposed to "a pointer that points to the object".

So I originally was about to assume that std::launder could magically fix the
cast between non-pointer-interconvertible objects in addition to its declared
purpose. Then I looked through all the WG21 working papers on std::launder I
could find:

1. Motivating example from 5/31/13:
https://groups.google.com/a/isocpp.org/forum/#!msg/std-proposals/93ebFsxCjvQ/myxPG6o_9pkJ

2. The original defect report #1776 from 2013-09-24
1776. Replacement of class objects containing reference member
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1776

3. The final C++14 "comment status" ruling to not include the fix to defect
#1776 to C++14 but validating it to be left open for fixing in the future
versions of the Standard
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3903.html#FI15

4. The defect reports that are supposed to be resolved resolved as by-products
of resolving defect #1776
1404. Object reallocation in unions
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1404
636. Dynamic type of objects and aliasing
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#636
2182. Pointer arithmetic in array-like containers
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2182
1116. Aliasing of union members
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1116

5. The original text of the solution, with explanatory example from 2014-11-21:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4303.html

6. The final text of the solution, (that by the way had to introduce
interconvertible pointers and put restrictions on what reinterpret_cast could
do) from 2016-06-23:
Core Issue 1776: Replacement of class objects containing reference members
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0137r1.html

(l)
*nowhere* in the above proceedings was std::launder:
- used; or
- claimed to be useful for; or
- motivated by a justification of its use for; or
- illustrated by an example
accessing a C++ object (say, `o2') via a result of reinterpet_cast (or an
equivalent sequence of static_casts) of a pointer to another object (say, `o1')
not pointer-interconvertible with `o2', to the pointer to the `o2'.

In fact the only complaint to std::launder known to me filed after C++17
adoption, "On std::launder" (see
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0532r0.pdf), presented,
among the others, the complaint similar to yours on non-practicality of always
stashing the value returned by placement operator new -- but nowhere did it
refer to using std::launder for the purpose mentioned above in (l) that you
claim it can fulfill. Never in its (rather voluminous) text (containing several
examples) was such casting mentioned even once.

All the above makes me believe that the use of an under-defined term "a pointer
value that points to an object" in std::launder definition is simply an
oversight and that the authors intended to use a (much better defined) term "a
pointer that points to an object" that sneaked in a series of revisions of
std::launder proposal made since the original 2014-11-21 revision where same
requirement was formulated as (obviously, incorrectly) "p represents the address
A of a byte in memory".

>
> In fact, until C++23 comes out, constructing nested objects using
> placement new within char arrays constructed on free store and then
> accessing them with reinterpret_cast and std::launder is the only
> technically correct way
(m) So you are changing the subject from Undefined behavior to (also undefined)
"technically correct way" and make an unsubstantiated statement about it. Nice
trick!

> you can construct containers which allocate
> their memory dynamically, including implementing std::vector:
> https://stackoverflow.com/questions/60465235/does-stdunitialized-copy-have-undefined-behavior
(n) anyone willing to actually follow your reference will find that neither the
answer nor even the question (!) mentions reinterpret_cast.

>
> I am satisfied that you are wrong and everyone else is right.
let's mark the above statement (o) :-)

>
> Try to trim your posts to a reasonable size.
I will consider trimming them to zero size because (m), (n) and especially (o)
above seem to indicate that you are arguing for the sake of arguing -- which
ironically may make one of your previous statements correct -- specifically that
this discussion was not going to be fruitful.

Pavel

unread,
Mar 1, 2020, 9:22:40 PM3/1/20
to
Chris Vine wrote:
> On Sun, 1 Mar 2020 18:58:00 -0500
> Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
>> Chris Vine wrote:
>>> On Sun, 1 Mar 2020 14:02:00 -0500
>>> Pavel <pauldont...@removeyourself.dontspam.yahoo> wrote:
>>>> Chris Vine wrote:
>>>>> On Sun, 1 Mar 2020 11:06:08 +0000
>>>>> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
>>>>>> The C++ standard does very definitely guarantee that the pointer values
>>>>>> are the same - §8.2.9/13 of C++17 read with the other provisions to
>>>>>> which I have referred.
>>>>>
>>>>> By the way the pointer values to which I was referring were those in
>>>>> the cppreference.com. Your version has the problem that the type
>>>>> providing storage is neither the same type as the replacement object nor
>>>>> an array of unsigned char or std::byte.
>>>>
>>>> I addressed the insignificance of the difference for the matter in hand in my
>>>> previous point.
>>>
>>> I don't think so. Your version has undefined behaviour.
>>
>> It is and that was the point. If the source pointer to an array or unsigned char
>> or std:byte, it still would have undefined behavior.
>
> You are going round in circles. If the source was an array of
> unsigned char or std:byte, it would not have undefined behaviour if you
> used reinterpret_cast with std::launder.
That code explicitly came contained this disclaimer

"
let's pretend
that launder and aliasing do not exist; we may have separate disagreement about
these but IMHO they are irrelevant here
"

Does it really feel good to take things out of context?

> The standard is clear on the
> point. You prove nothing by producing code which we both agree has
> undefined behaviour.
It's good we both agreed to that although for different reasons. Your delusions
about std::launder are explicitly addressed in another sub-branch of this
thread; of course given your own admission in that branch it is probably too
long for your to read it in full.
0 new messages