Spec-Zone.ru › C++

Инициализация по умолчанию

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

Синтаксис

T object ; (1)
new T (2)

Пояснение

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

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

Результаты инициализации по умолчанию:

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

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

(до C++11)

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

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

Тип T допускает конструирование по умолчанию с квалификатором const, если

  • T — std::meta::info.
(начиная с C++26)
  • T — std::nullptr_t.
(начиная с C++11)
  • Инициализация по умолчанию T вызвала бы пользовательский конструктор T (не унаследованный от базового класса)(начиная с C++11).
  • T — тип класса, для которого:
  • каждый прямой нестатический член данных T имеет тип класса X (или является массивом такого типа), X допускает конструирование по умолчанию с квалификатором const, и
  • T не имеет прямых вариантных членов, и
  • каждый потенциально создаваемый базовый класс T допускает конструирование по умолчанию с квалификатором const.
(до C++11)
  • каждый прямой нестатический член данных, не являющийся вариантом, T имеет инициализатор члена по умолчанию или тип, допускающий конструирование по умолчанию с квалификатором const;
  • если T — объединение с хотя бы одним нестатическим членом данных, ровно один вариантный член имеет инициализатор члена по умолчанию;
  • если T не является объединением, каждый член анонимного объединения допускает конструирование по умолчанию с квалификатором const; и
  • каждый потенциально создаваемый базовый класс T допускает конструирование по умолчанию с квалификатором const.
(начиная с C++11)
  • T — массив типа, допускающего инициализацию по умолчанию с квалификатором const.

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

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

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

(до C++26)

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

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

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

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

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

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

(начиная с C++26)

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

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

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

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

  • Неопределённым значением, если 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

См. также

  • преобразующий конструктор
  • конструктор по умолчанию
  • explicit
  • инициализация
    • агрегатная инициализация
    • константная инициализация
    • копирующая инициализация
    • прямая инициализация
    • инициализация списком
    • инициализация ссылки
    • инициализация значением
    • инициализация нулями
  • new

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

Spec-Zone.ru

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