Does a phantom type have the same alignment as the original one?

和自甴很熟 提交于 2019-12-03 01:14:06

The Standard mentions in [basic.align]/1:

Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated. An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated. An object type imposes an alignment requirement on every object of that type; stricter alignment can be requested using the alignment specifier (7.6.2).

Moreover, [basic.compound]/3, mentions:

The value representation of pointer types is implementation-defined. Pointers to layout-compatible types shall have the same value representation and alignment requirements (6.11). [Note: Pointers to over-aligned types (6.11) have no special representation, but their range of valid values is restricted by the extended alignment requirement].

As a result, there is a guarantee that layout-compatible types have the same alignment.

struct { T m; } and T are not layout-compatible.

As pointed here, in order for two elements to be layout compatible then they both have to be standard-layout types, and their non-static data members must occur with the same types and in the same order.

struct { T m; } contains just a T, but T is a T so it cannot contain a T as its first non-static data member.

According to the letter of the law, size and alignment of types is implementation-defined and the standard gives you few if any guarantees about what sizeof and alignof will return.

template <typename T, typename P>
struct Tagged {
    T value;
};

In theory, the compiler is permitted to add padding to the end of this struct, which would obviously alter the size and probably the alignment as well. In practise, the only time I could envisage this happening is if T was given some sort of compiler-specific "packed" attribute, but Tagged was not (but even then, GCC seems to work okay).

In any case, I'd say it would be a good idea to add some static asserts to ensure that the compiler is being sensible -- which is exactly what you've done :).

As mentioned by gsamaras, the standard guarantees the same alignment for layout compatible classes.

Unfortunately, struct { T m; } and T are not layout compatible.

In 12.2.21 the standard lays out the requirements for a layout compatible class:

Two standard-layout struct (Clause 12) types are layout-compatible classes if their common initial sequence comprises all members and bit-fields of both classes (6.9).

And the definition of common initial sequence is in 12.2.20:

The common initial sequence of two standard-layout struct (Clause 12) types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types and either neither entity is a bit-field or both are bit-fields with the same width. [Example:
struct A { int a; char b; };
struct B { const int b1; volatile char b2; };
struct C { int c; unsigned : 0; char b; };
struct D { int d; char b : 4; };
struct E { unsigned int e; char b; };
The common initial sequence of A and B comprises all members of either class. The common initial sequence of A and C and of A and D comprises the first member in each case. The common initial sequence of A and E is empty.
— end example]

So from this we can make the following important observations:

  1. Layout compatibility is limited strictly to standard layout classes. (Or enums use the same underlying type or the trivial case when T and T2 are literally the exact same type. See 6.9.11.) In the general case, T is not a standard layout class. In fact, T is not even a class in your example (it is a uint16_t, believe it or not, this matters according to the standard.)*
  2. Even if T is guaranteed to be a standard layout class, struct { T m; } does not have a common initial sequence with T. The sequence of struct { T m; } begins with T, whereas the sequence of T begins with whatever T's non-static data members are. This is actually strictly guaranteed not to be a T as a class cannot contain itself by value.

Therefore, the guarantee cannot be held by the letter of the standard. You should continue to perform the static_assertions to ensure your compiler is behaving in the fashion you expect.

* See most questions on union type punning.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!