For motivation, see P0840R0.
We propose the addition of an attribute, [[no_unique_address]] to indicates that a unique address is not required for an non-static data member of a class. A non-static data member with this attribute may share its address (or its tail padding) with another object, if it could when used as a base class.
Add a new paragraph before [intro.object] (6.6.2) paragraph 7:
A potentially-overlapping subobject is either:
- a base class subobject, or
- a non-static data member declared with the no_unique_address attribute ([dcl.attr.nouniqueaddr]).
Change in [intro.object] (6.6.2) paragraph 7:
An object has nonzero size if itOtherwise, if the object is a base class subobject of a standard-layout class type with no non-static data members, it has zero size. Otherwise, the circumstances under which the object has zero size are implementation-defined. Unless it is a bit-field (12.2.4),
- is not a potentially-overlapping subobject, or
- is not of class type, or
- is of a class type with virtual member functions or virtual base classes, or
- has subobjects of nonzero size or bit-fields of nonzero length.
a most derivedan objectshall have awith nonzero sizeandshall occupy one or more bytes of storage, including every byte that is occupied in full or in part by any of its subobjects.Base class subobjects may have zero size. An object of trivially copyable or standard-layout type (6.9) shall occupy contiguous bytes of storage.
Change in [intro.object] (6.6.2) paragraph 8:
Unless an object is a bit-field or abase classsubobject of zero size, the address of that object is the address of the first byte it occupies. Two objectsa and bwith overlapping lifetimes that are not bit-fields may have the same address if one is nested within the other, or if at least one is abase classsubobject of zero size and they are of different types; otherwise, they have distinct addresses and shall occupy disjoint bytes of storage. [Footnote] [Example] The address of a non-bit-field subobject of zero size is that of a byte of storage occupied by the complete object of the subobject.
Change in [basic.life] (6.6.3) bullet 8.4:
neither the original object nor the new object is a potentially-overlapping subobject (6.6.2 [intro.object])was a most derived object (6.6.2) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).
Change in [basic.types] (6.7) paragraph 2:
For any object (other than abase-classpotentially-overlapping subobject) of trivially copyable type […]
Change in [basic.types] (6.7) paragraph 3:
For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is abase-classpotentially-overlapping subobject, if the underlying bytes (6.6.1) making up obj1 are copied into obj2, obj2 shall subsequently hold the same value as obj1. […]
Change in [expr.sizeof] (8.5.2.3) paragraph 1:
The sizeof operator yields the number of bytes occupied by a non-potentially-overlapping object of the typein the object representationof its operand. […]
Change in [expr.sizeof] (8.5.2.3) paragraph 2:
When applied to a reference or a reference type, the result is the size of the referenced type. When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.The size of a most derived class shall be greater than zero (6.6.2).The result of applying sizeof to abase classpotentially-overlapping subobject is the size of thebase classtype, not the size of the subobject. [ Footnote: The actual size of abase classpotentially-overlapping subobject may be less than the result of applying sizeof to the subobject, due to virtual base classes and less strict padding requirements onbase classpotentially-overlapping subobjects. ] When applied to an array, the result is the total number of bytes in the array. This implies that the size of an array of n elements is n times the size of an element.
Change in [expr.rel] (8.5.9) paragraph 3:
Comparing unequal pointers to objects is defined as follows:
- […]
- If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause 14), neither member is a subobject of zero size, and
providedtheir class is not a union.- Otherwise, neither pointer compares greater than the other.
Add a new subclause [dcl.attr.nouniqueaddr] after [dcl.attr.noreturn]:
10.6.9 No unique address attribute [dcl.attr.nouniqueaddr]
The attribute-token no_unique_address specifies that a non-static data member is a potentially-overlapping subobject (6.6.2 [intro.object]). It shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may appertain to a non-static data member other than a bit-field.
[ Note: The non-static data member can share the address of another non-static data member or that of a base class, and any padding that would normally be inserted at the end of the object can be reused as storage for other members. — end note ] [ Example:
template<typename Key, typename Value, typename Hash, typename Pred, typename Allocator> class hash_map { [[no_unique_address]] Hash hasher; [[no_unique_address]] Pred pred; [[no_unique_address]] Allocator alloc; Bucket *buckets; // ... public: // ... };Here, hasher, pred, and alloc could have the same address as buckets if their respective types are all empty. — end example ]
Change in [class] (12) paragraph 4 and split into two paragraphs:
[ Note: Complete objectsand member subobjectsof class typeshallhave nonzero size.[ Footnote:Base class subobjects and members declared with the no_unique_address attribute ([dcl.attr.nouniqueaddr]) are not so constrained. ][ Note: ... ]
Change in [class] (12) paragraph 7:
[…][…] [Note: M(X) is the set of the types of all non-base-class subobjects that
- If X is a non-union class type
whose firstwith a non-static data memberhasof type X0 that is either of zero size or is the first non-static data member of X (where said member may be an anonymous union), the set M(X) consists of X0 and the elements of M(X0).are guaranteedmay be at a zero offset ina standard-layout class to be at a zero offset inX. —end note] […]
Change in [class.mem] (12.2) paragraph 21:
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, either both entities are declared with the no_unique_address attribute ([dcl.attr.nouniqueaddr]) or neither is, and eitherneither entity is a bit-field orboth entities are bit-fields with the same width or neither is a bit-field. [ Example ]
Change in [meta.unary.prop] (23.15.4.3) Table 42 ("Type property predicates"):
Template Condition Predicate … template <class T> struct is_empty; T is a class type, but not a union type, with no non-static data members other than bit-fields of length 0subobjects of zero size, no virtual member functions, no virtual base classes, and no base class B for which is_empty_v<B> is false.If T is a non-union class type, T shall be a complete type. …