Namespaces
Variants

Default-initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Это инициализация, выполняемая при создании объекта без инициализатора.

Содержание

Синтаксис

T object  ; (1)
new T (2)

Объяснение

Инициализация по умолчанию выполняется в трёх ситуациях:

1) когда переменная с автоматической, статической или потоково-локальной продолжительностью хранения объявляется без инициализатора;
2) когда объект с динамической продолжительностью хранения создаётся с помощью new-expression без инициализатора;
3) когда базовый класс или нестатический член данных не упоминается в списке инициализации конструктора и этот конструктор вызывается.

Эффекты инициализации по умолчанию:

  • если T является (возможно, cv-квалифицированным) не-POD (до C++11) классом, конструкторы рассматриваются и проходят разрешение перегрузки для пустого списка аргументов. Выбранный конструктор (который является одним из конструкторов по умолчанию ) вызывается для предоставления начального значения нового объекта;
  • если T является массивом, каждый элемент массива инициализируется по умолчанию;
  • в противном случае инициализация не выполняется (см. примечания ).

Инициализация по умолчанию константного объекта

Если программа требует инициализации по умолчанию объекта const -квалифицированного типа T , T должен быть классом с константной инициализацией по умолчанию или массивом таких классов.

Классный тип T является const-конструируемым по умолчанию, если инициализация по умолчанию T вызовет предоставленный пользователем конструктор T (не унаследованный от базового класса) (начиная с C++11) или если

Только (возможно, cv-квалифицированные) не-POD классовые типы (или их массивы) с автоматической продолжительностью хранения считались инициализированными по умолчанию, когда инициализатор не используется. Скалярные типы и POD типы с динамической продолжительностью хранения считались неинициализированными (начиная с C++11, эта ситуация была переклассифицирована как форма инициализации по умолчанию).

(until C++11)
  • каждый прямой нестатический член данных M типа T имеет классовый тип X (или массив таких), X является const-default-constructible, и
  • T не имеет прямых вариантных членов , и
(до C++11)
  • каждый прямой невариантный нестатический член данных M типа T имеет инициализатор члена по умолчанию или, если M имеет классовый тип X (или массив таких), X является const-default-constructible,
  • если T является объединением с хотя бы одним нестатическим членом данных, ровно один вариантный член имеет инициализатор члена по умолчанию,
  • если T не является объединением, для каждого анонимного члена-объединения с хотя бы одним нестатическим членом данных (если есть), ровно один нестатический член данных имеет инициализатор члена по умолчанию, и
(начиная с C++11)

каждый потенциально конструируемый базовый класс T является конструируемым по умолчанию как константный.

Неопределённые и ошибочные значения

При получении памяти для объекта с автоматической или динамической продолжительностью хранения, объект имеет неопределённое значение .

Если для объекта не выполняется инициализация, этот объект сохраняет неопределённое значение до тех пор, пока это значение не будет заменено.

(до C++26)

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

  • Если объект имеет динамическую продолжительность хранения или является объектом, связанным с переменной или параметром функции , первое объявление которого помечено [[ indeterminate ]] , байты имеют неопределённые значения .
  • В противном случае байты имеют ошибочные значения , где каждое значение определяется реализацией независимо от состояния программы.

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

  • Если любой бит в представлении значения имеет неопределённое значение, объект имеет неопределённое значение .
  • В противном случае, если любой бит в представлении значения имеет ошибочное значение, объект имеет ошибочное значение .
(начиная с C++26)

Если вычисление производит неопределённое значение, поведение является неопределённым .

Если вычисление приводит к ошибочному значению, поведение является ошибочным .

(since C++26)

Особые случаи

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

(начиная с C++17)
  • unsigned char
  • char , если его базовый тип является unsigned char

Для неопределённого или ошибочного (since C++26) значения value , неинициализированное результирующее значение для value равно:

  • Неопределённое значение, если value также является неопределённым значением.
  • value , если value является ошибочным значением.
(начиная с C++26)

Если вычисление eval производит неопределённое или ошибочное (since C++26) значение value типа, дружественного к неинициализированным данным, поведение является определённым в следующих случаях:

  • eval представляет собой вычисление одного из следующих выражений и операндов:
В этом случае результатом операции является неинициализированное значение результата value .
  • eval представляет собой вычисление правого операнда простого оператора присваивания , левый операнд которого является lvalue типа, дружественного к неинициализированным объектам.
В данном случае значение объекта, на который ссылается левый операнд, заменяется неинициализированным результирующим значением value .
  • eval представляет собой вычисление выражения инициализации при инициализации объекта типа, дружественного к неинициализированному состоянию.
  • value не может иметь тип std::byte если инициализируемый объект не имеет тип std::byte .
(начиная с C++17)
В данном случае этот объект инициализируется неинициализированным значением результата value .

Преобразование неопределенного значения типа, допускающего неинициализированное состояние, дает неопределенное значение.

Преобразование ошибочного значения типа, дружественного к неинициализированным значениям, дает ошибочное значение; результатом преобразования является значение преобразуемого операнда.

(since C++26)
// Случай 1: Неинициализированные объекты с динамической продолжительностью хранения
// Все версии C++: неопределённое значение + неопределённое поведение
int f(bool b)
{
    unsigned char* c = new unsigned char;
    unsigned char d = *c; // OK, "d" имеет неопределённое значение
    int e = d;            // неопределённое поведение
    return b ? d : 0;     // неопределённое поведение, если "b" истинно
}
// Случай 2: Неинициализированные объекты с автоматической продолжительностью хранения
// до C++26: неопределённое значение + неопределённое поведение
// с C++26: ошибочное значение + ошибочное поведение
int g(bool b)
{
    unsigned char c;     // "c" имеет неопределённое/ошибочное значение
    unsigned char d = c; // нет неопределённого/ошибочного поведения,
                         // но "d" имеет неопределённое/ошибочное значение
    assert(c == d);      // выполняется, но оба целочисленных продвижения имеют
                         // неопределённое/ошибочное поведение
    int e = d;           // неопределённое/ошибочное поведение
    return b ? d : 0;    // неопределённое/ошибочное поведение, если "b" истинно
}
// Аналогично случаю 2
void h()
{
    int d1, d2;  // "d1" и "d2" имеют неопределённые/ошибочные значения
    int e1 = d1; // неопределённое/ошибочное поведение
    int e2 = d1; // неопределённое/ошибочное поведение
    assert(e1 == e2); // выполняется
    assert(e1 == d1); // выполняется, неопределённое/ошибочное поведение
    assert(e2 == d1); // выполняется, неопределённое/ошибочное поведение
    // нет неопределённого/ошибочного поведения,
    // но "d2" имеет неопределённое/ошибочное значение
    std::memcpy(&d2, &d1, sizeof(int));
    assert(e1 == d2); // выполняется, неопределённое/ошибочное поведение
    assert(e2 == d2); // выполняется, неопределённое/ошибочное поведение
}

Примечания

Ссылки и константные скалярные объекты не могут быть инициализированы по умолчанию.

Макрос тестирования возможностей Значение Стандарт Возможность
__cpp_constexpr 201907L (C++20) Тривиальная инициализация по умолчанию и asm-декларация в constexpr функциях

Пример

#include <string>
struct T1 { int mem; };
struct T2
{
    int mem;
    T2() {} // "mem" отсутствует в списке инициализации
};
int n; // статическая не-классовая переменная, выполняется двухфазная инициализация:
       // 1) инициализация нулями устанавливает n в ноль
       // 2) инициализация по умолчанию ничего не делает, оставляя n равным нулю
int main()
{
    [[maybe_unused]]
    int n;            // не-классовая переменная, значение неопределено
    std::string s;    // класс, вызывает конструктор по умолчанию, значение равно ""
    std::string a[2]; // массив, инициализирует элементы по умолчанию, значение равно {"", ""}
//  int& r;           // Ошибка: ссылка
//  const int n;      // Ошибка: константная не-классовая переменная
//  const T1 t1;      // Ошибка: константный класс с неявным конструктором по умолчанию
    [[maybe_unused]]
    T1 t1;            // класс, вызывает неявный конструктор по умолчанию
    const T2 t2;      // константный класс, вызывает предоставленный пользователем конструктор по умолчанию
                      // t2.mem инициализируется по умолчанию
}

Отчеты о дефектах

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

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 178 C++98 не было инициализации значением;
пустой инициализатор вызывал инициализацию по умолчанию
(хотя new T ( ) также выполняет обнуляющую инициализацию)
пустой инициализатор вызывает
инициализацию значением
CWG 253 C++98 инициализация по умолчанию константного объекта не могла
вызывать неявно объявленный конструктор по умолчанию
разрешено, если все подобъекты инициализированы
CWG 616 C++98 преобразование lvalue в rvalue любого
неинициализированного объекта всегда было UB
неопределённый unsigned char разрешён
CWG 1787 C++98 чтение неопределённого unsigned char
кэшированного в регистре было UB
сделано корректно определённым

Смотрите также