Challenging eerorika's answer, I believe you are guaranteed binary compatibility. I'll reference the C++11 spec for this.
Key pieces:
[class/7] This defines a standard-layout class. It's pretty clear we all agree that these are standard layout.
[intro.object/5] and [intro.object/6]
An object of trivially copyable or standard-layout
type (3.9) shall occupy contiguous bytes of storage.
Unless an object is a bit-field or a base class subobject of zero size, the address of that object is the address of
the first byte it occupies.
This bounds the shapes that a standard-layout object can have, and specifies what we can call "the address of" an object.
[class.mem/20]
A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its
initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note:
There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning,
as necessary to achieve appropriate alignment. —end note ]
This says that we can at least convert a ID_t*
to an int*
via reinterpret cast.
Now, you assert that sizeof(ID_t) == sizeof(int)
. This is good news because it limits your options. int* someIdAsInt = reinterpret_cast<int*>(&someId)
is guaranteed to succeed, and it will point at the first member, per class.mem. So the question is, what are the possible addresses that can be returned? Obviously, there is only one address which can possibly be the first byte of sizeof(int)
bytes, which is, of course, the address of someId
.
So we can be certain that &someId
and someIdAsInt
refer to the same address. And, in particular, someIdAsInt
must point at the initial member per class.mem.
If I were to do *someIdAsInt = 43
, the result must be the same as if I did someId.raw_id = 43
, because someIdAsInt
points at someId.raw_id
. This statement must be true no matter what I do with this pointer to obscure it.
This says that *someIdAsInt
and someId
either must have the same layout (permitting the assignment), or the compiler must track the value of someIdAsInt
, treating it different than a normal int*
. This is why I depart from eerorika's answer. This information could not be handled in the type system with type tagging(it would force the compiler to be able to track tags, even if you did brutal things like pass int*
between threads). So any information tagging must be baked into the bytes forming the value of the int*
. The C++ spec does not say anything about the format of a pointer's value.
However, there are limits to how different int*
can be, which are generally speaking, undisputed. The key one is that I can use std::memcpy
to copy the bytes of one int
into another, and the resulting integer must be the same value. To the best of my knowledge, this is not actually written into the spec, but it is accepted by (basically?) all programmers as a common law rule of C and C++. Indeed this sort of thing is further emphasized by the inclusion of std::bit_cast
in C++20. To have two integer formats which cannot be distinguished by their bytes would break all sorts of things.
So, if you accept this common law ruling in a language-lawyer argument, then the layout of your ID_t
must be identical to the layout of int
if sizeof(ID_t) == sizeof(int)
. If that common law ruling is not accepted then... well... I'd just say some soul searching is in order =D
Note that this does not mean that you can safely go the other way. If you have an int
array, you cannot cast it to ID_t*
and then access those. That would be a violation of strict aliasing, as there was never an ID_t
in that memory address in the first place. However, because they are identical layouts, using std::memcpy
or std::bit_cast
to convert to an ID_t
with an equivalent bit pattern would still be fair game.
Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations (9.7.1), or layout-compatible standard-layout class types (11.4).
Looks like no. You can compare two class types for layout compatibility but not a scaler type and a class type. – Trichostandard-layout
types andsizeof(int)
==alignof(int)
then you should expect them to be compatible as the compiler is not allowed to add padding at the beginning of the object and has no need to add padding at the end. And I have not come across a system where thealignof(int)
differs from its size. – Trichoint
they are guaranteed to be the same as each other. Assuming they fulfill the requirements of astandard-layout
class. Also if the two types where in a union then the memberraw_id
could be accessed legally from either object if set via either of them. – Trichoint*
toID_t*
may affect or be affected by the alias analysis. – Artillery