Spec-Zone.ru › C++

Тип

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

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

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

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

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

Типы группируются в различные категории на основе их свойств:

  • типы объектов — (возможно, квалифицированные cv) типы, которые не являются типами функций, типами ссылок или, возможно, квалифицированными cv типом void (см. также std::is_object);
  • скалярные типы — (возможно, квалифицированные cv) типы объектов, которые не являются типами массивов или типами классов (см. также std::is_scalar);
  • тривиальные типы (см. также std::is_trivial), типы POD (см. также std::is_pod), типы литералов (см. также std::is_literal_type) и другие категории, указанные в библиотеке признаков типов или как названные требования к типам.

Создание полного типа объекта, при котором количество байтов в его представлении объекта не может быть представлено в типе std::size_t (т.е. тип результата оператора sizeof) приводит к ошибке.

Имена типов

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

  • объявления класса;
  • объявления объединения;
  • объявления перечисления;
  • объявления 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 может использоваться с некоторыми изменениями в следующих ситуациях:

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

Развёрнутый спецификатор типа

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

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

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

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

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

Если какое-либо выражение 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&, за исключением преобразования из константы нулевого указателя или из указателя на, возможно, cv-квалифицированный void;
  • оператор доступа к члену класса, применённый к выражению типа T;
  • typeid, sizeof или alignof оператор, применённый к типу T;
  • арифметический оператор, применённый к указателю на T;
  • определение класса с базовым классом T;
  • присваивание lvalue типа T;
  • блок catch для исключения типа T, T& или T*.

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

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

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

  • Тип класса (например, class X) может считаться неполным в одном месте в единице трансляции и полным позже; тип class X является одним и тем же типом в обоих точках:
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 1464 C++98 размер объекта может быть непредставимым в std::size_t такой тип является некорректным
CWG 2006 C++98 cv-квалифицированные типы void были типом объекта и полным типом исключены из обеих категорий
CWG 2448 C++98 только cv-неквалифицированные типы могли быть целочисленными и типом с плавающей точкой разрешены cv-квалифицированные типы
CWG 2630 C++98 неясно, считается ли класс полным вне
единицы трансляции, где появляется определение класса
класс является полным
если его определение
доступно в этом случае
CWG 2643 C++98 тип указателя на массив неизвестной длины
не мог быть завершен (но он уже завершён)
тип указанного массива
не может быть завершен

См. также

C документация для Типа

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

Spec-Zone.ru

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