3 Basic concepts [basic]

3.9 Types [basic.types]

Note: [basic.types] and the subclauses thereof impose requirements on implementations regarding the representation of types. There are two kinds of types: fundamental types and compound types. Types describe objects ([intro.object]), references ([dcl.ref]), or functions ([dcl.fct]).  — end note ]

For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes ([intro.memory]) making up the object can be copied into an array of char or unsigned char.43 If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [ Example:

#define N sizeof(T)
char buf[N];
T obj;                          // obj initialized to its original value
std::memcpy(buf, &obj, N);      // between these two calls to std::memcpy,
                                // obj might be modified
std::memcpy(&obj, buf, N);      // at this point, each subobject of obj of scalar type
                                // holds its original value

 — end example ]

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 a base-class subobject, if the underlying bytes ([intro.memory]) making up obj1 are copied into obj2,44 obj2 shall subsequently hold the same value as obj1. [ Example:

T* t1p;
T* t2p;
    // provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
    // at this point, every subobject of trivially copyable type in *t1p contains
    // the same value as the corresponding subobject in *t2p

 — end example ]

The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T. For trivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values.45

A class that has been declared but not defined, an enumeration type in certain contexts ([dcl.enum]), or an array of unknown size or of incomplete element type, is an incompletely-defined object type.46 Incompletely-defined object types and cv void are incomplete types ([basic.fundamental]). Objects shall not be defined to have an incomplete type.

A class type (such as “class X”) might be incomplete at one point in a translation unit and complete later on; the type “class X” is the same type at both points. The declared type of an array object might be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type. The declared type of an array object might be an array of unknown bound and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points (“array of unknown bound of T” and “array of N T”) are different types. The type of a pointer to array of unknown size, or of a type defined by a typedef declaration to be an array of unknown size, cannot be completed. [ Example:

class X;                        // X is an incomplete type
extern X* xp;                   // xp is a pointer to an incomplete type
extern int arr[];               // the type of arr is incomplete
typedef int UNKA[];             // UNKA is an incomplete type
UNKA* arrp;                     // arrp is a pointer to an incomplete type
UNKA** arrpp;

void foo() {
  xp++;                         // ill-formed: X is incomplete
  arrp++;                       // ill-formed: incomplete type
  arrpp++;                      // OK: sizeof UNKA* is known
}

struct X { int i; };            // now X is a complete type
int  arr[10];                   // now the type of arr is complete

X x;
void bar() {
  xp = &x;                      // OK; type is “pointer to X  arrp = &arr;                  // ill-formed: different types
  xp++;                         // OK:  X is complete
  arrp++;                       // ill-formed: UNKA can't be completed
}

 — end example ]

Note: The rules for declarations and expressions describe in which contexts incomplete types are prohibited.  — end note ]

An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not cv void.

Arithmetic types ([basic.fundamental]), enumeration types, pointer types, pointer to member types ([basic.compound]), std::nullptr_t, and cv-qualified versions of these types ([basic.type.qualifier]) are collectively called scalar types. Scalar types, POD classes (Clause [class]), arrays of such types and cv-qualified versions of these types ([basic.type.qualifier]) are collectively called POD types. Cv-unqualified scalar types, trivially copyable class types (Clause [class]), arrays of such types, and cv-qualified versions of these types ([basic.type.qualifier]) are collectively called trivially copyable types. Scalar types, trivial class types (Clause [class]), arrays of such types and cv-qualified versions of these types ([basic.type.qualifier]) are collectively called trivial types. Scalar types, standard-layout class types (Clause [class]), arrays of such types and cv-qualified versions of these types ([basic.type.qualifier]) are collectively called standard-layout types.

A type is a literal type if it is:

  • possibly cv-qualified void; or

  • a scalar type; or

  • a reference type; or

  • an array of literal type; or

  • a possibly cv-qualified class type (Clause [class]) that has all of the following properties:

    • it has a trivial destructor,

    • it is either a closure type ([expr.prim.lambda]), an aggregate type ([dcl.init.aggr]), or has at least one constexpr constructor or constructor template (possibly inherited ([namespace.udecl]) from a base class) that is not a copy or move constructor,

    • if it is a union, at least one of its non-static data members is of non-volatile literal type, and

    • if it is not a union, all of its non-static data members and base classes are of non-volatile literal types.

Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations ([dcl.enum]), or layout-compatible standard-layout class types ([class.mem]).

By using, for example, the library functions ([headers]) std::memcpy or std::memmove.

By using, for example, the library functions ([headers]) std::memcpy or std::memmove.

The intent is that the memory model of C++ is compatible with that of ISO/IEC 9899 Programming Language C.

The size and layout of an instance of an incompletely-defined object type is unknown.

3.9.1 Fundamental types [basic.fundamental]

Objects declared as characters (char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or signed. Plain char, signed char, and unsigned char are three distinct types, collectively called narrow character types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements ([basic.align]); that is, they have the same object representation. For narrow character types, all bits of the object representation participate in the value representation. [ Note: A bit-field of narrow character type whose length is larger than the number of bits in the object representation of that type has padding bits; see [class.bit].  — end note ] For unsigned narrow character types, each possible bit pattern of the value representation represents a distinct number. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined. For each value i of type unsigned char in the range 0 to 255 inclusive, there exists a value j of type char such that the result of an integral conversion ([conv.integral]) from i to char is j, and the result of an integral conversion from j to unsigned char is i.

There are five standard signed integer types : signed char”, “short int”, “int”, “long int”, and “long long int”. In this list, each type provides at least as much storage as those preceding it in the list. There may also be implementation-defined extended signed integer types. The standard and extended signed integer types are collectively called signed integer types. Plain ints have the natural size suggested by the architecture of the execution environment47; the other signed integer types are provided to meet special needs.

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: unsigned char”, “unsigned short int”, “unsigned int”, “unsigned long int”, and “unsigned long long int”, each of which occupies the same amount of storage and has the same alignment requirements ([basic.align]) as the corresponding signed integer type48; that is, each signed integer type has the same object representation as its corresponding unsigned integer type. Likewise, for each of the extended signed integer types there exists a corresponding extended unsigned integer type with the same amount of storage and alignment requirements. The standard and extended unsigned integer types are collectively called unsigned integer types. The range of non-negative values of a signed integer type is a subrange of the corresponding unsigned integer type, and the value representation of each corresponding signed/unsigned type shall be the same. The standard signed integer types and standard unsigned integer types are collectively called the standard integer types, and the extended signed integer types and extended unsigned integer types are collectively called the extended integer types. The signed and unsigned integer types shall satisfy the constraints given in the C standard, section 5.2.4.2.1.

Unsigned integers shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.49

Type wchar_t is a distinct type whose values can represent distinct codes for all members of the largest extended character set specified among the supported locales ([locale]). Type wchar_t shall have the same size, signedness, and alignment requirements ([basic.align]) as one of the other integral types, called its underlying type. Types char16_t and char32_t denote distinct types with the same size, signedness, and alignment as uint_least16_t and uint_least32_t, respectively, in <cstdint>, called the underlying types.

Values of type bool are either true or false.50Note: There are no signed, unsigned, short, or long bool types or values.  — end note ] Values of type bool participate in integral promotions ([conv.prom]).

Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.51 A synonym for integral type is integer type. The representations of integral types shall define values by use of a pure binary numeration system.52Example: this International Standard permits two's complement, ones' complement and signed magnitude representations for integral types.  — end example ]

There are three floating-point types: float, double, and long double. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double. The value representation of floating-point types is implementation-defined. Note: This International Standard imposes no requirements on the accuracy of floating-point operations; see also [limits].  — end note ] Integral and floating types are collectively called arithmetic types. Specializations of the standard library template std::numeric_limits ([support.limits]) shall specify the maximum and minimum values of each arithmetic type for an implementation.

A type cv void is an incomplete type that cannot be completed; such a type has an empty set of values. It is used as the return type for functions that do not return a value. Any expression can be explicitly converted to type cv void ([expr.cast]). An expression of type cv void shall be used only as an expression statement ([stmt.expr]), as an operand of a comma expression ([expr.comma]), as a second or third operand of ?: ([expr.cond]), as the operand of typeid, noexcept, or decltype, as the expression in a return statement ([stmt.return]) for a function with the return type cv void, or as the operand of an explicit conversion to type cv void.

A value of type std::nullptr_t is a null pointer constant ([conv.ptr]). Such values participate in the pointer and the pointer to member conversions ([conv.ptr], [conv.mem]). sizeof(std::nullptr_t) shall be equal to sizeof(void*).

Note: Even if the implementation defines two or more basic types to have the same value representation, they are nevertheless different types.  — end note ]

int must also be large enough to contain any value in the range [INT_MIN, INT_MAX], as defined in the header <climits>.

See [dcl.type.simple] regarding the correspondence between types and the sequences of type-specifiers that designate them.

This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.

Using a bool value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false.

Therefore, enumerations ([dcl.enum]) are not integral; however, enumerations can be promoted to integral types as specified in [conv.prom].

A positional representation for integers that uses the binary digits 0 and 1, in which the values represented by successive bits are additive, begin with 1, and are multiplied by successive integral power of 2, except perhaps for the bit with the highest position. (Adapted from the American National Dictionary for Information Processing Systems.)

3.9.2 Compound types [basic.compound]

Compound types can be constructed in the following ways:

These methods of constructing types can be applied recursively; restrictions are mentioned in [dcl.ptr], [dcl.array], [dcl.fct], and [dcl.ref]. Constructing a type such that the number of bytes in its object representation exceeds the maximum value representable in the type std::size_t ([support.types]) is ill-formed.

The type of a pointer to cv void or a pointer to an object type is called an object pointer type. [ Note: A pointer to void does not have a pointer-to-object type, however, because void is not an object type.  — end note ] The type of a pointer that can designate a function is called a function pointer type. A pointer to objects of type T is referred to as a “pointer to T”. [ Example: a pointer to an object of type int is referred to as “pointer to int ” and a pointer to an object of class X is called a “pointer to X”.  — end example ] Except for pointers to static members, text referring to “pointers” does not apply to pointers to members. Pointers to incomplete types are allowed although there are restrictions on what can be done with them ([basic.align]). Every value of pointer type is one of the following:

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 ([intro.memory]) occupied by the object54 or the first byte in memory after the end of the storage occupied by the object, respectively. [ Note: A pointer past the end of an object ([expr.add]) is not considered to point to an unrelated object of the object's type that might be located at that address. A pointer value becomes invalid when the storage it denotes reaches the end of its storage duration; see [basic.stc].  — end note ] For purposes of pointer arithmetic ([expr.add]) and comparison ([expr.rel], [expr.eq]), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n]. The value representation of pointer types is implementation-defined. Pointers to layout-compatible types shall have the same value representation and alignment requirements ([basic.align]). [ Note: Pointers to over-aligned types ([basic.align]) have no special representation, but their range of valid values is restricted by the extended alignment requirement. — end note ]

Two objects a and b are pointer-interconvertible if:

  • they are the same object, or

  • one is a standard-layout union object and the other is a non-static data member of that object ([class.union]), or

  • one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object ([class.mem]), or

  • there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.

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 ([expr.reinterpret.cast]). [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address.  — end note ]

A pointer to cv-qualified ([basic.type.qualifier]) or cv-unqualified void can be used to point to objects of unknown type. Such a pointer shall be able to hold any object pointer. An object of type cv void* shall have the same representation and alignment requirements as cv char*.

Static class members are objects or functions, and pointers to them are ordinary pointers to objects or functions.

For an object that is not within its lifetime, this is the first byte in memory that it will occupy or used to occupy.

3.9.3 CV-qualifiers [basic.type.qualifier]

A type mentioned in [basic.fundamental] and [basic.compound] is a cv-unqualified type. Each type which is a cv-unqualified complete or incomplete object type or is void ([basic.types]) has three corresponding cv-qualified versions of its type: a const-qualified version, a volatile-qualified version, and a const-volatile-qualified version. The term object type ([intro.object]) includes the cv-qualifiers specified in the decl-specifier-seq ([dcl.spec]), declarator (Clause [dcl.decl]), type-id ([dcl.name]), or new-type-id ([expr.new]) when the object is created.

  • A const object is an object of type const T or a non-mutable subobject of such an object.

  • A volatile object is an object of type volatile T, a subobject of such an object, or a mutable subobject of a const volatile object.

  • A const volatile object is an object of type const volatile T, a non-mutable subobject of such an object, a const subobject of a volatile object, or a non-mutable volatile subobject of a const object.

The cv-qualified or cv-unqualified versions of a type are distinct types; however, they shall have the same representation and alignment requirements ([basic.align]).55

A compound type ([basic.compound]) is not cv-qualified by the cv-qualifiers (if any) of the types from which it is compounded. Any cv-qualifiers applied to an array type affect the array element type, not the array type ([dcl.array]).

See [dcl.fct] and [class.this] regarding function types that have cv-qualifiers.

There is a partial ordering on cv-qualifiers, so that a type can be said to be more cv-qualified than another. Table [tab:relations.on.const.and.volatile] shows the relations that constitute this ordering.

Table 10 — Relations on const and volatile
no cv-qualifier < const
no cv-qualifier < volatile
no cv-qualifier < const volatile
const < const volatile
volatile < const volatile

In this International Standard, the notation cv (or cv1, cv2, etc.), used in the description of types, represents an arbitrary set of cv-qualifiers, i.e., one of {const}, {volatile}, {const, volatile}, or the empty set. For a type cv T, the top-level cv-qualifiers of that type are those denoted by cv. [ Example: The type corresponding to the type-id const int& has no top-level cv-qualifiers. The type corresponding to the type-id volatile int * const has the top-level cv-qualifier const. For a class type C, the type corresponding to the type-id void (C::* volatile)(int) const has the top-level cv-qualifier volatile.  — end example ]

Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T”, where T is an array type, refers to an array whose elements are so-qualified. An array type whose elements are cv-qualified is also considered to have the same cv-qualifications as its elements.Example:

typedef char CA[5];
typedef const char CC;
CC arr1[5] = { 0 };
const CA arr2 = { 0 };

The type of both arr1 and arr2 is “array of 5 const char”, and the array type is considered to be const-qualified.  — end example ]

The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and non-static data members of unions.