Spec-Zone.ru › C++

Тип

Объекты, ссылки, функции, включая специализации шаблонов функций, и выражения обладают свойством, называемым типом, которое как ограничивает операции, разрешённые для этих сущностей, так и придаёт семантическое значение в остальном обобщённым последовательностям битов.

Классификация типов

Система типов C++ состоит из следующих типов:

  • фундаментальные типы (см. также std::is_fundamental):
    • тип void (см. также std::is_void);
  • тип std::nullptr_t (см. также std::is_null_pointer);
(начиная с C++11)
  • тип std::meta::info (см. также std::is_reflection);
(начиная с C++26)
  • арифметические типы (см. также std::is_arithmetic):
  • целочисленные типы (включая версии с cv-квалификацией, см. также std::is_integral; синоним целочисленного типа — целый тип):
  • тип bool;
  • символьные типы:
  • узкие символьные типы:
    • обычные символьные типы: char, signedchar, unsignedchar[1]
  • тип char8_t
(начиная с C++20)
  • широкие символьные типы: char16_t, char32_t, (начиная с C++11)wchar_t;
  • знаковые целочисленные типы:
    • стандартные знаковые целочисленные типы: signedchar, short, int, long, longlong(начиная с C++11);
  • расширенные знаковые целочисленные типы (определяемые реализацией);
(начиная с C++11)
  • беззнаковые целочисленные типы:
    • стандартные беззнаковые целочисленные типы: unsignedchar, unsignedshort, unsigned, unsignedlong, unsignedlonglong(начиная с C++11);
  • расширенные беззнаковые целочисленные типы (каждый соответствует расширенному знаковому целочисленному типу, и наоборот);
(начиная с C++11)
  • типы с плавающей точкой (см. также std::is_floating_point):
    • стандартные типы с плавающей точкой: float, double, longdouble и их версии с cv-квалификацией;
  • расширенные типы с плавающей точкой (включая версии с cv-квалификацией):
    • типы с плавающей точкой фиксированной ширины;
    • другие расширенные типы с плавающей точкой, определяемые реализацией;
(начиная с C++23)
  • составные типы (см. также std::is_compound):
  • ссылочные типы (см. также std::is_reference):
  • типы ссылок на lvalue (см. также std::is_lvalue_reference):
    • ссылка на lvalue типа объекта;
    • ссылка на lvalue типа функции;
  • типы ссылок на rvalue (см. также std::is_rvalue_reference):
    • ссылка на rvalue типа объекта;
    • ссылка на rvalue типа функции;
(начиная с C++11)
  • типы указателей (см. также std::is_pointer):
  • типы указателей на объекты:
    • типы указателей на объекты;
    • типы указателей на void;
  • типы указателей на функции;
  • типы указателей на члены (см. также 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_scoped_enum);
(начиная с C++11)
  • типы классов:
    • типы, не являющиеся объединениями (см. также std::is_class);
    • типы объединений (см. также std::is_union).
  1. signedchar и unsignedchar являются узкими символьными типами, но не являются символьными типами. Другими словами, множество узких символьных типов не является подмножеством множества символьных типов.

Для каждого типа без cv-квалификации, кроме ссылочных типов и типов функций, система типов поддерживает три дополнительные версии с cv-квалификацией этого типа (const, volatile и constvolatile).

Другие категории

Тип объекта (см. также std::is_object) — это тип (возможно, с cv-квалификацией), который не является типом функции, ссылочным типом или типом void (возможно, с cv-квалификацией).

Следующие типы вместе называются скалярными типами (см. также std::is_scalar):

  • арифметические типы
  • типы перечислений
  • типы указателей
  • типы указателей на члены
  • std::nullptr_t
(начиная с C++11)
  • std::meta::info
(начиная с C++26)
  • версии этих типов с cv-квалификацией

Следующие типы вместе называются типами неявного времени жизни:

  • скалярные типы
  • типы классов неявного времени жизни
  • типы массивов
  • версии этих типов с cv-квалификацией

Следующие типы вместе называются тривиально копируемыми типами:

  • скалярные типы
  • тривиально копируемые типы классов
  • массивы таких типов
  • версии этих типов с cv-квалификацией

Следующие типы вместе называются типами стандартного размещения:

  • скалярные типы
  • типы классов стандартного размещения
  • массивы таких типов
  • версии этих типов с cv-квалификацией
(начиная с C++11)
Иерархическая диаграмма свойств типов

Примечание: элементы изображения SVG можно нажимать, но сначала необходимо открыть диаграмму в новой вкладке браузера

Устаревшие категории

Следующие типы вместе называются POD-типами (см. также std::is_pod):

  • скалярные типы
  • POD-классы
  • массивы таких типов
  • версии этих типов с cv-квалификацией
(устарело в C++20)

Следующие типы вместе называются тривиальными типами (см. также std::is_trivial):

  • скалярные типы
  • тривиальные типы классов
  • массивы таких типов
  • версии этих типов с cv-квалификацией
(начиная с C++11)
(устарело в C++26)

Тип, определяемый программой

Специализация, определяемая программой, — это явная специализация или частичная специализация, которая не входит в стандартную библиотеку C++ и не определена реализацией.

Типом, определяемым программой, является один из следующих типов:

  • Класс или перечисление, не являющиеся замыканием(начиная с C++11) типа класса или типа перечисления, которые не входят в стандартную библиотеку C++ и не определены реализацией.
  • Тип замыкания лямбда-выражения, не предоставленного реализацией.
(начиная с C++11)
  • Инстанцирование специализации, определяемой программой.

Именование типов

Имя может быть объявлено как обозначающее тип с помощью:

  • объявления класса;
  • объявления объединения;
  • объявления перечисления;
  • объявления typedef;
  • объявления псевдонима типа.

Типы, у которых нет имён, часто необходимо обозначать в программах на C++; соответствующий синтаксис называется type-id. Синтаксис type-id, обозначающего тип T, в точности совпадает с синтаксисом объявления переменной или функции типа T, за исключением того, что идентификатор опущен, decl-specifier-seq в грамматике объявления ограничена type-specifier-seq, а новые типы можно определять только в том случае, если type-id находится в правой части объявления псевдонима типа, не являющегося шаблоном.

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

Часть declarator грамматики объявления без имени называется abstract-declarator.

Type-id может использоваться в следующих ситуациях:

  • для указания целевого типа в выражениях приведения типов;
  • в качестве аргументов для sizeof, alignof, alignas, new и typeid;
  • в правой части объявления псевдонима типа;
  • в качестве завершающего типа возвращаемого значения в объявлении функции;
  • в качестве аргумента по умолчанию параметра типа шаблона;
  • в качестве аргумента шаблона для параметра типа шаблона;
  • в динамической спецификации исключений.
(до C++17)

В следующих ситуациях type-id может использоваться с некоторыми изменениями:

  • в списке параметров функции (если имя параметра опущено) type-id использует decl-specifier-seq вместо type-specifier-seq (в частности, разрешены некоторые спецификаторы класса хранения);
  • в имени пользовательской функции преобразования абстрактный декларатор не может содержать операторы функции или массива.

Уточнённый спецификатор типа

Уточнённые спецификаторы типа могут использоваться для ссылки на ранее объявленное имя класса (class, struct или union) или ранее объявленное имя перечисления, даже если это имя было скрыто объявлением не-типа. Их также можно использовать для объявления новых имён классов.

Подробности см. в разделе Уточнённый спецификатор типа.

Статический тип

Тип выражения, определяемый в результате анализа программы во время компиляции, называется статическим типом выражения. Статический тип не меняется во время выполнения программы.

Динамический тип

Если некоторое glvalue-выражение ссылается на полиморфный объект, тип его наиболее производного объекта называется динамическим типом.

// 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

Для prvalue-выражений динамический тип всегда совпадает со статическим типом.

Неполный тип

Следующие типы являются неполными типами:

  • тип void (возможно, с cv-квалификацией);
  • неполностью определённые типы объектов:
    • тип класса, который был объявлен (например, с помощью предварительного объявления), но не определён;
    • массив неизвестного размера;
    • массив элементов неполного типа;
    • тип перечисления с момента объявления до определения его базового типа.

Все остальные типы являются полными.

Каждый из следующих контекстов требует, чтобы тип T был полным:

  • определение функции или вызов функции с типом возвращаемого значения T или типом аргумента T;
  • определение объекта типа T;
  • объявление нестатического члена-данного класса типа T;
  • выражение new для объекта типа T или массива, тип элемента которого — T;
  • преобразование lvalue в rvalue, применённое к glvalue типа T;
  • неявное или явное преобразование к типу T;
  • стандартное преобразование, dynamic_cast или static_cast к типу T* или T&, за исключением преобразования из константы нулевого указателя или из указателя на void, возможно с cv-квалификацией;
  • оператор доступа к члену класса, применённый к выражению типа T;
  • typeid, sizeof или alignof, применённый к типу T;
  • арифметический оператор, применённый к указателю на T;
  • определение класса с базовым классом T;
  • присваивание lvalue типа T;
  • обработчик типа T, T& или T*.

(Как правило, когда должен быть известен размер и размещение T.)

Если одна из этих ситуаций возникает в единице трансляции, определение типа должно находиться в той же единице трансляции. В противном случае это не требуется.

Неполностью определённый тип объекта можно завершить:

  • Тип класса (например, classX) может считаться неполным в одной точке единицы трансляции и полным в более поздней точке; тип classX в обеих точках является одним и тем же типом:
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
}
  • Объявленный тип объекта-массива может быть массивом элементов неполного типа класса и поэтому быть неполным; если тип класса позднее завершается в единице трансляции, тип массива становится полным; тип массива в этих двух точках является одним и тем же типом.
  • Объявленный тип объекта-массива может быть массивом неизвестного размера и поэтому быть неполным в одной точке единицы трансляции и полным в более поздней; типы массивов в этих двух точках («массив неизвестного размера из T» и «массив из N T») являются разными типами.

Тип указателя или ссылки на массив неизвестного размера постоянно указывает на неполный тип или ссылается на него. Массив неизвестного размера, указанный в объявлении typedef, постоянно обозначает неполный тип. В обоих случаях тип массива нельзя завершить:

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
}

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применён к Поведение согласно опубликованной версии Корректное поведение
CWG 328 C++98 члены класса неполного типа не были запрещены
если объект типа класса никогда не создавался
нестатические члены-данные класса
должны быть полными
CWG 977 C++98 момент, когда тип перечисления становится
полным в своём определении, был неясен
тип является полным, как только
определён базовый тип
CWG 1362 C++98 пользовательские преобразования к типу T* или T& требовали, чтобы T был полным не требуется
CWG 2006 C++98 типы void с cv-квалификацией являлись типами объектов и полными типами исключены из обеих категорий
CWG 2448 C++98 целочисленными типами и типами с плавающей точкой могли быть только типы без cv-квалификации допускаются типы с cv-квалификацией
CWG 2630 C++98 было неясно, считается ли класс полным за пределами
единицы трансляции, в которой находится его определение
в этом случае класс является полным,
если его определение
доступно
CWG 2643 C++98 тип указателя на массив неизвестного размера
нельзя было завершить (хотя он уже является полным)
тип массива, на который указывает указатель,
нельзя завершить
LWG 2139 C++98 значение термина «тип, определяемый пользователем» было неясно вместо него определяется и используется термин
«тип, определяемый программой»
LWG 3119 C++11 было неясно, являются ли типы замыканий типами, определяемыми программой это было уточнено

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
    • 6.8.2 Фундаментальные типы [basic.fundamental]
  • Стандарт C++20 (ISO/IEC 14882:2020):
    • 6.8.2 Фундаментальные типы [basic.fundamental]
  • Стандарт C++17 (ISO/IEC 14882:2017):
    • 6.9.1 Фундаментальные типы [basic.fundamental]
  • Стандарт C++14 (ISO/IEC 14882:2014):
    • 3.9.1 Фундаментальные типы [basic.fundamental]
  • Стандарт C++11 (ISO/IEC 14882:2011):
    • 3.9.1 Фундаментальные типы [basic.fundamental]
  • Стандарт C++98 (ISO/IEC 14882:1998):
    • 3.9.1 Фундаментальные типы [basic.fundamental]

См. также

Свойства типов Интерфейсы на основе шаблонов, позволяющие запрашивать свойства типов во время компиляции
Документация по C о типах

Внешние ссылки

1. Дерево типов C++0x Говарда Хиннанта

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/incomplete_type

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API