Инициализация по умолчанию
Это инициализация, выполняемая при создании объекта без инициализатора.
Синтаксис
T object ; | (1) | |
new T | (2) |
Пояснение
Инициализация по умолчанию выполняется в трёх случаях:
Результаты инициализации по умолчанию:
- если
T— класс (возможно, с cv-квалификацией) не-POD-типа(до C++11), рассматриваются конструкторы и к ним применяется разрешение перегрузки для пустого списка аргументов. Выбранный конструктор (один из конструкторов по умолчанию) вызывается для задания начального значения нового объекта; - если
T— массив, каждый его элемент инициализируется по умолчанию;
| (начиная с C++26) |
- в противном случае инициализация не выполняется (см. примечания).
| До C++11 считалось, что без инициализатора инициализируются по умолчанию только классы (возможно, с cv-квалификацией) не-POD-типа (или массивы таких типов) с автоматической длительностью хранения. Считалось, что скалярные типы и POD-типы с динамической длительностью хранения не инициализируются (начиная с C++11 эта ситуация классифицируется как разновидность инициализации по умолчанию). | (до C++11) |
Инициализация по умолчанию константного объекта
Если программа требует инициализации по умолчанию объекта типа T, квалифицированного как const, тип T должен допускать конструирование по умолчанию с квалификатором const.
Тип T допускает конструирование по умолчанию с квалификатором const, если
| (начиная с C++26) |
| (начиная с C++11) |
- Инициализация по умолчанию
Tвызвала бы пользовательский конструкторT(не унаследованный от базового класса)(начиная с C++11). -
T— тип класса, для которого:
| (до C++11) |
| (начиная с C++11) |
-
T— массив типа, допускающего инициализацию по умолчанию с квалификатором const.
Неопределённые и ошибочные значения
| При выделении хранилища для объекта с автоматической или динамической длительностью хранения объект имеет неопределённое значение. Если инициализация объекта не выполняется, объект сохраняет неопределённое значение, пока оно не будет заменено. | (до C++26) |
| При выделении хранилища для объекта с автоматической или динамической длительностью хранения байты, составляющие хранилище объекта, имеют следующее начальное значение:
Если инициализация объекта (включая подобъекты) не выполняется, такой байт сохраняет начальное значение, пока оно не будет заменено.
| (начиная с C++26) |
Если вычисление даёт неопределённое значение, поведение является неопределённым.
| Если вычисление даёт ошибочное значение, поведение является ошибочным. | (начиная с C++26) |
Особые случаи
Следующие типы являются дружественными к неинициализированным значениям:
| (начиная с C++17) |
- unsignedchar
- char, если его базовый тип — unsignedchar
Для неопределённого или ошибочного(начиная с C++26) значения value результирующее значение неинициализированного объекта value является:
- Неопределённым значением, если
valueтакже является неопределённым значением.
| (начиная с C++26) |
Если вычисление даёт неопределённое или ошибочное(начиная с C++26) значение value типа, дружественного к неинициализированным значениям, поведение определено, если вычисление представляет собой:
- вычисление одного из следующих выражений или операндов:
- Второй или третий операнд условного выражения.
- Правый операнд выражения с оператором «запятая».
- Операнд преобразования целочисленного типа, явного преобразования или
static_castк типу, дружественному к неинициализированным значениям. - Выражение с отбрасываемым значением.
- В этом случае результат операции является результирующим значением неинициализированного объекта
value.
- вычисление правого операнда простого оператора присваивания, левый операнд которого является lvalue типа, дружественного к неинициализированным значениям.
value. - вычисление выражения инициализации при инициализации объекта типа, дружественного к неинициализированным значениям.
value. Преобразование неопределённого значения типа, дружественного к неинициализированным значениям, даёт неопределённое значение.
| Преобразование ошибочного значения типа, дружественного к неинициализированным значениям, даёт ошибочное значение; результат преобразования является значением преобразованного операнда. | (начиная с C++26) |
// Case 1: Uninitialized objects with dynamic storage duration
// All C++ versions: indeterminate value + undefined behavior
int f(bool b)
{
unsigned char* c = new unsigned char;
unsigned char d = *c; // OK, “d” has an indeterminate value
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if “b” is true
}
// Case 2: Uninitialized objects with automatic storage duration
// until C++26: indeterminate value + undefined behavior
// since C++26: erroneous value + erroneous behavior
int g(bool b)
{
unsigned char c; // “c” has an indeterminate/erroneous value
unsigned char d = c; // no undefined/erroneous behavior,
// but “d” has an indeterminate/erroneous value
assert(c == d); // holds, but both integral promotions have
// undefined/erroneous behavior
int e = d; // undefined/erroneous behavior
return b ? d : 0; // undefined/erroneous behavior if “b” is true
}
// Same as case 2
void h()
{
int d1, d2; // “d1” and “d2” have indeterminate/erroneous values
int e1 = d1; // undefined/erroneous behavior
int e2 = d1; // undefined/erroneous behavior
assert(e1 == e2); // holds
assert(e1 == d1); // holds, undefined/erroneous behavior
assert(e2 == d1); // holds, undefined/erroneous behavior
// no undefined/erroneous behavior,
// but “d2” has an indeterminate/erroneous value
std::memcpy(&d2, &d1, sizeof(int));
assert(e1 == d2); // holds, undefined/erroneous behavior
assert(e2 == d2); // holds, undefined/erroneous behavior
}
Примечания
Ссылки и константные скалярные объекты не могут инициализироваться по умолчанию.
| Макрос проверки поддержки функции | Значение | Стандарт | Функция |
|---|---|---|---|
__cpp_constexpr | 201907L | (C++20) | Тривиальная инициализация по умолчанию и объявление asm в функциях constexpr |
Пример
#include <string>
struct T1 { int mem; };
struct T2
{
int mem;
T2() {} // “mem” is not in the initializer list
};
int n; // static non-class, a two-phase initialization is done:
// 1) zero-initialization initializes n to zero
// 2) default-initialization does nothing, leaving n being zero
int main()
{
[[maybe_unused]]
int n; // non-class, the value is indeterminate
std::string s; // class, calls default constructor, the value is ""
std::string a[2]; // array, default-initializes the elements, the value is {"", ""}
// int& r; // Error: a reference
// const int n; // Error: a const non-class
// const T1 t1; // Error: const class with implicit default constructor
[[maybe_unused]]
T1 t1; // class, calls implicit default constructor
const T2 t2; // const class, calls the user-provided default constructor
// t2.mem is default-initialized
}
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применён к | Опубликованное поведение | Правильное поведение |
|---|---|---|---|
| CWG 178 | C++98 | инициализации значением не существовало; пустой инициализатор вызывал инициализацию по умолчанию (хотя new T() также выполняет инициализацию нулями) | пустой инициализатор вызывает инициализацию значением |
| CWG 253 | C++98 | инициализация константного объекта по умолчанию не могла вызывать неявно объявленный конструктор по умолчанию | разрешено, если инициализированы все подобъекты |
| CWG 616 | C++98 | преобразование lvalue в rvalue для любого неинициализированного объекта всегда приводило к неопределённому поведению | неопределённое значение unsignedchar допускается |
| CWG 1787 | C++98 | чтение неопределённого unsignedchar, сохранённого в регистре, приводило к неопределённому поведению | сделано определённым |
| CWG 3089 | C++11 |
std::nullptr_t не допускает инициализацию по умолчанию с квалификатором const | допускает инициализацию по умолчанию с квалификатором const |
См. также
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/default_initialization