C++ Logo


Advanced search

Re: On "transparently replaceable" in std::vector operations

From: Tom Honermann <tom_at_[hidden]>
Date: Fri, 25 Jun 2021 00:22:15 -0400
On 6/23/21 3:02 AM, language.lawyer--- via Std-Discussion wrote:
> On 30/04/2021 18:07, Tom Honermann via Std-Discussion wrote:
>> On 4/30/21 10:16 AM, Hyman Rosen via Std-Discussion wrote:
>>> This is where the conceptual error of the C++ object model shows. A
>>> pointer or reference points to memory, not to an object. The pointer
>>> type informs how that memory should be treated. If the memory
>>> remains allocated to the program, the pointer is valid. If the
>>> contents of the memory are valid for a type, the object of that type
>>> in that memory is valid.
>> I tried to point this out elsewhere in this thread, but I'm not sure
>> the point landed.
>> If the C++ object model worked as you state it should, then constexpr
>> evaluation would not be feasible or would be more constrained than it
>> currently is. With a memory based model, the implementation would
>> have to mimic memory layout for the target architecture in order for
>> code to behave the same at compile-time vs run-time. The C++ object
>> model avoids that by allowing the implementation to represent objects
>> differently during constant evaluation, but in a way that isn't
>> observable.
> Given the following program:
> union U {
> struct S1 { int i; int j; } s1;
> struct S2 { char c[8]; } s2;
> } u;
> static_assert((void*)&u.s1.j == (void*)&u.s2.c[4]);
> and the fact that run-time assert with the same condition would not
> fail, how should static_assert Ā«behaveĀ»?
> 1) It is not allowed to fail
> 2) It is allowed to fail
> 3) The condition is not a constant expression
> If 1), how is it possible without mimicking memory layout?
> If 2), how does it agree with the fact that code must behave the same
> at compile-time vs run-time?
> If 3), why?

I stated "memory layout" where I should have stated "object
representation" above. Inspecting the value of u or one of its
sub-objects within the static_assert declaration requires declaring u as
constexpr, initializing it, and then consistently accessing only the
active member of u (though not all compilers diagnose access of an
inactive member, at least not in all cases). The example above is
well-formed because it does not actually attempt to access the object
representation of u.s1.j; it just asks the question of whether it shares
an address with u.s2.c[4]. Since u has static storage duration and the
implementation necessarily knows its memory layout (it couldn't generate
code to access members or be able to compute sizeof(u) otherwise), the
question can be answered statically.

So, the answer for this particular example is 1 because the compiler
does in fact know the memory layout (it doesn't have to mimic it).
Similar examples that attempt to poke at the object representation of a
value during constant evaluation are ill-formed and will be diagnosed
(modulo implementation defects). The motivation for that restriction is
to prevent any possibility of UB during constant evaluation because that
could lead to UB within a compiler itself.


Received on 2021-06-24 23:22:17