Инициализация значением
Это инициализация, выполняемая при создании объекта с пустым инициализатором.
Синтаксис
T () | (1) | |
new T () | (2) | |
Class::Class(...) : member () { ... } | (3) | |
T object {}; | (4) | (начиная с C++11) |
T {} | (5) | (начиная с C++11) |
new T {} | (6) | (начиная с C++11) |
Class::Class(...) : member {} { ... } | (7) | (начиная с C++11) |
Пояснение
Инициализация значением выполняется в следующих ситуациях:
Во всех случаях, если используется пустая пара фигурных скобок {} и T является агрегатным типом, вместо инициализации значением выполняется агрегатная инициализация.
| Если | (начиная с C++11) |
Инициализация значением оказывает следующие эффекты:
- Если
T— тип класса (возможно, cv-квалифицированный): - Если при инициализации по умолчанию для
Tвыбирается конструктор, и этот конструктор не является объявленным пользователем(до C++11)предоставленным пользователем(начиная с C++11), объект сначала инициализируется нулём. - В любом случае объект инициализируется по умолчанию.
- В противном случае, если
Tявляется типом массива, каждый элемент массива инициализируется значением. - В противном случае объект инициализируется нулём.
Примечания
Синтаксис T object(); не инициализирует объект; он объявляет функцию без аргументов, возвращающую T. До C++11 именованную переменную можно было инициализировать значением с помощью T object = T();: в этом случае создаётся временный объект, инициализируемый значением, а затем объект копирующе инициализируется из него; большинство компиляторов оптимизируют копирование в этом случае.
Ссылки нельзя инициализировать значением.
Как описано в разделе приведение в стиле вызова функции, синтаксис T() (1) запрещён, если T обозначает тип массива, тогда как T{} (5) разрешён.
Все стандартные контейнеры (std::vector, std::list и т. д.) инициализируют свои элементы значением при создании с одним аргументом size_type или при увеличении размера вызовом resize(), если только их аллокатор не изменяет поведение construct.
Пример
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
struct T1
{
int mem1;
std::string mem2;
virtual void foo() {} // make sure T1 is not an aggregate
}; // implicit default constructor
struct T2
{
int mem1;
std::string mem2;
T2(const T2&) {} // user-provided copy constructor
}; // no default constructor
struct T3
{
int mem1;
std::string mem2;
T3() {} // user-provided default constructor
};
std::string s{}; // class => default-initialization, the value is ""
int main()
{
int n{}; // scalar => zero-initialization, the value is 0
assert(n == 0);
double f = double(); // scalar => zero-initialization, the value is 0.0
assert(f == 0.0);
int* a = new int[10](); // array => value-initialization of each element
assert(a[9] == 0); // the value of each element is 0
T1 t1{}; // class with implicit default constructor =>
assert(t1.mem1 == 0); // t1.mem1 is zero-initialized, the value is 0
assert(t1.mem2 == ""); // t1.mem2 is default-initialized, the value is ""
// T2 t2{}; // error: class with no default constructor
T3 t3{}; // class with user-provided default constructor =>
std::cout << t3.mem1; // t3.mem1 is default-initialized to indeterminate value
assert(t3.mem2 == ""); // t3.mem2 is default-initialized, the value is ""
std::vector<int> v(3); // value-initialization of each element
assert(v[2] == 0); // the value of each element is 0
std::cout << '\n';
delete[] a;
}
Возможный вывод:
42
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение согласно опубликованной версии | Исправленное поведение |
|---|---|---|---|
| CWG 178 | C++98 | инициализации значением не существовало; пустой инициализатор вызывал инициализацию по умолчанию (хотя new T() также выполняет инициализацию нулём) | пустой инициализатор вызывает инициализацию значением |
| CWG 543 | C++98 | инициализация значением объекта класса без каких-либо предоставленных пользователем конструкторов была эквивалентна инициализации значением каждого подобъекта (при которой член с предоставленным пользователем конструктором по умолчанию мог не инициализироваться нулём) | инициализирует нулём весь объект, а затем вызывает конструктор по умолчанию |
| CWG 1301 | C++11 | инициализация значением объединений с удалёнными конструкторами по умолчанию приводила к инициализации нулём | они инициализируются по умолчанию |
| CWG 1368 | C++98 | любой предоставленный пользователем конструктор приводил к пропуску инициализации нулём | инициализация нулём пропускается только при наличии предоставленного пользователем конструктора по умолчанию |
| CWG 1502 | C++11 | инициализация значением объединения без предоставленного пользователем конструктора по умолчанию инициализировала нулём только объект, несмотря на наличие инициализаторов членов по умолчанию | после инициализации нулём выполняется инициализация по умолчанию |
| CWG 1507 | C++98 | при инициализации значением объекта класса без каких-либо предоставленных пользователем конструкторов не проверялась корректность конструктора по умолчанию, если он был тривиальным | корректность тривиального конструктора по умолчанию проверяется |
| CWG 2820 | C++98 | инициализация по умолчанию после инициализации нулём требовала нетривиальный конструктор | не требуется |
| CWG 2859 | C++98 | инициализация значением объекта класса могла включать инициализацию нулём, даже если при инициализации по умолчанию фактически не выбирался предоставленный пользователем конструктор | в этом случае инициализация нулём не выполняется |
См. также
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/value_initialization