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.
The C++ type system consists of the following types:
std::is_fundamental
): void
(see also std::is_void
); std::nullptr_t
(since C++11) (see also std::is_null_pointer
); std::is_arithmetic
): float
, double
, long double
) (see also std::is_floating_point
); std::is_integral
): bool
; char
, signed char
, unsigned char
) char8_t
char16_t
, char32_t
, wchar_t
); short int
, int
, long int
, long long int
); unsigned short int
, unsigned int
, unsigned long int
, unsigned long long int
); std::is_compound
): std::is_reference
): std::is_lvalue_reference
): std::is_rvalue_reference
): std::is_pointer
): std::is_member_pointer
): std::is_member_object_pointer
); std::is_member_function_pointer
); std::is_array
); std::is_function
); std::is_enum
); std::is_class
); std::is_union
). For every type other than reference and function, the type system supports three additional cv-qualified versions of that type (const
, volatile
, and const volatile
).
Types are grouped in various categories based on their properties:
void
(see also std::is_object
); std::is_scalar
); std::is_trivial
), POD types (see also std::is_pod
), literal types (see also std::is_literal_type
), and other categories listed in the the type traits library or as named type requirements. A name can be declared to refer to a type by means of:
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:
Type-id can be used with some modifications in the following situations:
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.
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.
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.
The following types are incomplete types:
void
(possibly cv-qualified); Any of the following contexts requires class T
to be complete:
T
or argument type T
; T
; T
; T
or an array whose element type is T
; T
; T
; T*
or T&
, except when converting from the null pointer constant or from a pointer to void; T
; T
; T
; T
; T
; 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:
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: class X; // X is an incomplete type extern X* xp; // xp is a pointer to an incomplete type void foo() { xp++; // ill-formed: X is incomplete } struct X { int i; }; // now X is a complete type X x; void bar() { xp = &x; // OK: type is “pointer to X” xp++; // OK: X is complete }
T
" and "array of N
T
") are different types. The type of a pointer to array of unknown bound, or of a type defined by a typedef
declaration to be an array of unknown bound, cannot be completed.
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
http://en.cppreference.com/w/cpp/language/incomplete_type