# Type

From cppreference.com
< cpp‎ | language

Objects, references, functions including function template specializations, and expressions have a property called type, which both restricts the operations that are permitted for those entities and provides semantic meaning to the otherwise generic sequences of bits.

### Type classification

The C++ type system consists of the following types:

- fundamental types (see also std::is_fundamental):

  - the type void (see also std::is_void);

  - the type std::nullptr_t (see also std::is_null_pointer);

(since C++11)

  - arithmetic types (see also std::is_arithmetic):
    - integral types (including cv-qualified versions, see also std::is_integral, a synonym for integral type is integer type):
      - the type bool;
      - character types:
        - narrow character types:
          - ordinary character types: char, signed char, unsigned char[1]

          - the type char8_t

(since C++20)

        - wide character types: char16_t, char32_t, (since C++11)wchar_t;
      - signed integer types:
        - standard signed integer types: signed char, short, int, long, long long;

        - extended signed integer types (implementation-defined);

(since C++11)

      - unsigned integer types:
        - standard unsigned integer types: unsigned char, unsigned short, unsigned, unsigned long, unsigned long long;

        - extended unsigned integer types (each corresponds to an extended signed integer type, and vice versa);

(since C++11)

    - floating-point types (see also std::is_floating_point):
      - standard floating-point types: float, double, long double and their cv-qualified versions;

      - extended floating-point types (including cv-qualified versions):
        - fixed width floating-point types;
        - other implementation-defined extended floating-point types;

(since C++23)

- compound types (see also std::is_compound):

  - reference types (see also std::is_reference):
    - lvalue reference types (see also std::is_lvalue_reference):
      - lvalue reference to object types;
      - lvalue reference to function types;

    - rvalue reference types (see also std::is_rvalue_reference):
      - rvalue reference to object types;
      - rvalue reference to function types;

(since C++11)

  - pointer types (see also std::is_pointer):
    - pointer-to-object types;
    - pointer-to-function types;
  - pointer-to-member types (see also std::is_member_pointer):
    - pointer-to-data-member types (see also std::is_member_object_pointer);
    - pointer-to-member-function types (see also std::is_member_function_pointer);
  - array types (see also std::is_array);
  - function types (see also std::is_function);
  - enumeration types (see also std::is_enum);
    - unscoped enumeration types;

    - scoped enumeration types (see also `std::is_scoped_enum`);

(since C++11)

  - class types:
    - non-union types (see also std::is_class);
    - union types (see also std::is_union).

1. ↑ signed char and unsigned char are narrow character types, but they are not character types. In other words, the set of narrow character types is not a subset of the set of character types.

For every non-cv-qualified type other than reference and function, the type system supports three additional cv-qualified versions of that type (const, volatile, and const volatile).

### Other categories

An object type (see also std::is_object) is a (possibly cv-qualified) type that is not a function type, not a reference type, and not (possibly cv-qualified) void.

The following types are collectively called scalar types (see also std::is_scalar):

- arithmetic types
- enumeration types
- pointer types
- pointer-to-member types

- std::nullptr_t

(since C++11)

- cv-qualified versions of these types

The following types are collectively called implicit-lifetime types:

- scalar types
- implicit-lifetime class types
- array types
- cv-qualified versions of these types

The following types are collectively called trivially copyable types:

- scalar types
- trivially copyable class types
- arrays of such types
- cv-qualified versions of these types

The following types are collectively called standard-layout types:

- scalar types
- standard-layout class types
- arrays of such types
- cv-qualified versions of these types

(since C++11)

Type traits hierarchy diagram
Note: Elements of SVG image are clickable, but you must open the diagram in a new browser tab first

#### Deprecated categories

The following types are collectively called POD types (see also std::is_pod):

- scalar types
- POD classes
- arrays of such types
- cv-qualified versions of these types

(deprecated in C++20)

The following types are collectively called trivial types (see also std::is_trivial):

- scalar types
- trivial class types
- arrays of such types
- cv-qualified versions of these types

(since C++11)
(deprecated in C++26)

### Program-defined type

A program-defined specialization is an explicit specialization or partial specialization that is not part of the C++ standard library and not defined by the implementation.

A program-defined type is one of the following types:

- A non-closure (since C++11) class type or enumeration type that is not part of the C++ standard library and not defined by the implementation.

- A closure type of a non-implementation-provided lambda expression.

(since C++11)

- An instantiation of a program-defined specialization.

### Type naming

A name can be declared to refer to a type by means of:

- class declaration;
- union declaration;
- enum declaration;
- typedef declaration;
- type alias declaration.

Types that do not have names often need to be referred to in C++ programs; the syntax for that is known as type-id. The syntax of the type-id that names type `T` is exactly the syntax of a declaration of a variable or function of type `T`, with the identifier omitted, except that decl-specifier-seq of the declaration grammar is constrained to type-specifier-seq, and that new types may be defined only if the type-id appears on the right-hand side of a non-template type alias declaration.

    int* p;               // declaration of a pointer to int
    static_cast<int*>(p); // type-id is "int*"

    int a[3];   // declaration of an array of 3 int
    new int[3]; // type-id is "int[3]" (called new-type-id)

    int (*(*x[2])())[3];      // declaration of an array of 2 pointers to functions
                              // returning pointer to array of 3 int
    new (int (*(*[2])())[3]); // type-id is "int (*(*[2])())[3]"

    void f(int);                    // declaration of a function taking int and returning void
    std::function<void(int)> x = f; // type template parameter is a type-id "void(int)"
    std::function<auto(int) -> void> y = f; // same

    std::vector<int> v;       // declaration of a vector of int
    sizeof(std::vector<int>); // type-id is "std::vector<int>"

    struct { int x; } b;         // creates a new type and declares an object b of that type
    sizeof(struct { int x; });   // error: cannot define new types in a sizeof expression
    using t = struct { int x; }; // creates a new type and declares t as an alias of that type

    sizeof(static int); // error: storage class specifiers not part of type-specifier-seq
    std::function<inline void(int)> f; // error: neither are function specifiers

The declarator part of the declaration grammar with the name removed is referred to as abstract-declarator.

Type-id may be used in the following situations:

- to specify the target type in cast expressions;
- as arguments to `sizeof`, `alignof`, `alignas`, `new`, and `typeid`;
- on the right-hand side of a type alias declaration;
- as the trailing return type of a function declaration;
- as the default argument of a template type parameter;
- as the template argument for a template type parameter;

- in dynamic exception specification.

(until C++17)

Type-id can be used with some modifications in the following situations:

- in the parameter list of a function (when the parameter name is omitted), type-id uses decl-specifier-seq instead of type-specifier-seq (in particular, some storage class specifiers are allowed);
- in the name of a user-defined conversion function, the abstract declarator cannot include function or array operators.

| This section is incomplete Reason: 8.2[dcl.ambig.res] if it can be compactly summarized

| This section is incomplete Reason: mention and link to decltype and auto

### Elaborated type specifier

Elaborated type specifiers may be used to refer to a previously-declared class name (class, struct, or union) or to a previously-declared enum name even if the name was hidden by a non-type declaration. They may also be used to declare new class names.

See elaborated type specifier for details.

### Static type

The type of an expression that results from the compile-time analysis of the program is known as the static type of the expression. The static type does not change while the program is executing.

### Dynamic type

If some glvalue expression refers to a polymorphic object, the type of its most derived object is known as the dynamic type.

    // given
    struct B { virtual ~B() {} }; // polymorphic type
    struct D : B {};               // polymorphic type

    D d; // most-derived object
    B* ptr = &d;

    // the static type of (*ptr) is B
    // the dynamic type of (*ptr) is D

For prvalue expressions, the dynamic type is always the same as the static type.

### Incomplete type

The following types are incomplete types:

- the type void (possibly cv-qualified);
- incompletely-defined object types:
  - class type that has been declared (e.g. by forward declaration) but not defined;
  - array of unknown bound;
  - array of elements of incomplete type;
  - enumeration type from the point of declaration until its underlying type is determined.

All other types are complete.

Any of the following contexts requires type `T` to be complete:

- definition of or call to a function with return type `T` or argument type `T`;
- definition of an object of type `T`;
- declaration of a non-static class data member of type `T`;
- `new` expression for an object of type `T` or an array whose element type is `T`;
- lvalue-to-rvalue conversion applied to a glvalue of type `T`;
- an implicit or explicit conversion to type `T`;
- a standard conversion, `dynamic_cast`, or `static_cast` to type T* or T&, except when converting from the null pointer constant or from a pointer to possibly cv-qualified void;
- class member access operator applied to an expression of type `T`;
- `typeid`, `sizeof`, or `alignof` operator applied to type `T`;
- arithmetic operator applied to a pointer to `T`;
- definition of a class with base class `T`;
- assignment to an lvalue of type `T`;
- a handler of type `T`, T&, or T*.

(In general, when the size and layout of `T` must be known.)

If any of these situations occur in a translation unit, the definition of the type must appear in the same translation unit. Otherwise, it is not required.

An incompletely-defined object type can be completed:

- A class type (such as class X) might be regarded as incomplete at one point in a translation unit and regarded as complete later on; the type class X is the same type at both points:

    struct X;            // declaration of X, no definition provided yet
    extern X* xp;        // xp is a pointer to an incomplete type:
                         // the definition of X is not reachable

    void foo()
    {
        xp++;            // ill-formed: X is incomplete
    }

    struct X { int i; }; // definition of X
    X x;                 // OK: the definition of X is reachable

    void bar()
    {
        xp = &x;         // OK: type is “pointer to X”
        xp++;            // OK: X is complete
    }

- 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 or reference to array of unknown bound permanently points to or refers to an incomplete type. An array of unknown bound named by a `typedef` declaration permanently refers to an incomplete type. In either case, the array type cannot be completed:

    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()
    {
        arrp++;         // error: UNKA is an incomplete type
        arrpp++;        // OK: sizeof UNKA* is known
    }

    int arr[10];        // now the type of arr is complete

    void bar()
    {
        arrp = &arr;    // OK: qualification conversion (since C++20)
        arrp++;         // error: UNKA cannot be completed
    }

### Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR | Applied to | Behavior as published | Correct behavior
--- | --- | --- | ---
CWG 328 | C++98 | class members of incomplete type were not prohibited if an object of the class type was never created | non-static class data members need to be complete
CWG 977 | C++98 | the point when an enumeration type becomes complete in its definition was unclear | the type is complete once the underlying type is determined
CWG 1362 | C++98 | user-defined conversions to type `T*` or `T&` required `T` to be complete | not required
CWG 2006 | C++98 | cv-qualified void types were object type and complete type | excluded from both categories
CWG 2448 | C++98 | only cv-unqualified types could be integral and floating-point types | allows cv-qualified types
CWG 2630 | C++98 | it was unclear whether a class is considered complete outside the translation unit where the definition of the class appears | the class is complete if its definition is reachable in this case
CWG 2643 | C++98 | the type of a pointer to array of unknown bound could not be completed (but it is already complete) | the pointed-to array type cannot be completed
LWG 2139 | C++98 | the meaning of “user-defined type” was unclear | defines and uses “program- defined type” instead
LWG 3119 | C++11 | it was unclear whether closure types are program-defined types | made clear

### References

- C++23 standard (ISO/IEC 14882:2024):

  - 6.8.2 Fundamental types [basic.fundamental]

- C++20 standard (ISO/IEC 14882:2020):

  - 6.8.2 Fundamental types [basic.fundamental]

- C++17 standard (ISO/IEC 14882:2017):

  - 6.9.1 Fundamental types [basic.fundamental]

- C++14 standard (ISO/IEC 14882:2014):

  - 3.9.1 Fundamental types [basic.fundamental]

- C++11 standard (ISO/IEC 14882:2011):

  - 3.9.1 Fundamental types [basic.fundamental]

- C++98 standard (ISO/IEC 14882:1998):

  - 3.9.1 Fundamental types [basic.fundamental]

### See also

Type traits | A compile-time template-based interfaces to query the properties of types
C documentation for Type

### External links

1. | Howard Hinnant's C++0x type tree

Retrieved from "https://en.cppreference.com/mwiki/index.php?title=cpp/language/type&oldid=179409"