Это особенно касается C ++ 11:
#include <iostream>
struct A {
A(){}
int i;
};
struct B : public A {
int j;
};
int main() {
B b = {};
std::cout << b.i << b.j << std::endl;
}
Компиляция с g ++ 8.2.1:
$ g++ -std=c++11 -pedantic-errors -Wuninitialized -O2 a.cpp
a.cpp: In function ‘int main()’:
a.cpp:25:25: warning: ‘b.B::<anonymous>.A::i’ is used uninitialized in this function [-Wuninitialized]
std::cout << b.i << " " << b.j << std::endl
gcc определяет b.i
как неинициализированный, но я думаю,она должна инициализироваться нулями вместе с b.j
.
То, что, по моему мнению, происходит (в частности, C ++ 11, из рабочего проекта ISO / IEC N3337, выделено мной):
B
не является агрегатом, поскольку имеет базовый класс.Общедоступные базовые классы были разрешены только в агрегатах в C ++ 17. A
не является агрегатом, поскольку в нем есть предоставленный пользователем конструктор
Раздел 8.5.1
Агрегат - это массив или класс (раздел 9) с без предоставленных пользователем конструкторов (12.1), без инициалов-скобок или равных для нестатических элементов данных (9.2)), нет личных или защищенных нестатических элементов данных (пункт 11), нет базовых классов (пункт 10) и нет виртуальных функций (10.3).
b
получает список, инициализированный пустым списком фигурных скобок
Раздел 8.5.4
Определена инициализация списка объекта или ссылки типа Tследующим образом:
- Если список инициализаторов не имеет элементов и T является типом класса с конструктором по умолчанию, объект инициализируется значением .
- В противном случае, если T является агрегатом,инициализация агрегата выполняется (8.5.1).
- Это означает
b
получает инициализированное значение B
имеет неявно определенный конструктор по умолчанию, поэтому b
инициализация значения вызывает инициализацию с нуля b.B::A
инициализируется с нуля, что инициализирует с нуляb.B::A.i
, а затем b.B::j
инициализируется нулями.
Раздел 8.5
Инициализация объекта или ссылки типа T с нуля означает:
...
- если T является (возможно, cv-квалифицированный) тип класса, не являющийся объединением, каждый элемент нестатических данных и каждый подобъект базового класса инициализируются нулями , а заполнение инициализируется нулевыми битами;
...
Инициализация значения объекта типа T означает:
- если T является (возможно, cv-квалифицированным) типом класса (раздел 9) с предоставленным пользователем конструктором (12.1), тогда вызывается конструктор по умолчанию для T (и инициализация некорректна, если у T нет доступного конструктора по умолчанию);
- , если T является (возможно, cv-квалифицированным) типом класса без объединения безпредоставленный пользователем конструктор, тогда объект инициализируется нулем и, если неявно объявленный конструктор по умолчанию T не является тривиальным, этот конструктор вызывается.
Однако, похоже, что gccговоря, что только b.B::j
будет инициализироваться нулем.Почему это?
Одна из причин, по которой я могу придумать, заключается в том, что B
обрабатывается как совокупность, которая инициализирует b.B::A
пустым списком.B
, безусловно, не является агрегатом, потому что gcc справедливо выдает ошибку, если мы пытаемся использовать агрегатную инициализацию.
// ... as in the above example
int main() {
B b = {A{}, 1};
std::cout << b.i << " " << b.j << std::endl;
}
Компиляция с C ++ 11
$ g++ -std=c++11 -pedantic-errors -Wuninitialized -O2 a.cpp
a.cpp: In function ‘int main()’:
a.cpp:10:18: error: could not convert ‘{A(), 1}’ from ‘<brace-enclosed initializer list>’ to ‘B’
B b = {A{}, 1};
Компиляция с C++ 17
g++ -std=c++17 -pedantic-errors -Wuninitialized -O2 a.cpp
a.cpp: In function ‘int main()’:
a.cpp:11:25: warning: ‘b.B::<anonymous>.A::i’ is used uninitialized in this function [-Wuninitialized]
std::cout << b.i << " " << b.j << std::endl;
И мы можем видеть, что b.i
неинициализирован, потому что B
является агрегатом, а b.B::A
инициализируется выражением, которое само по себе оставляет A::i
неинициализированным.
Так что это не совокупность.Другая причина в том, что b.B::j
инициализируется нулями, а b.B::A
инициализируется значениями, но я не вижу этого нигде в спецификации.
Последняя причина, если более старая версиястандарт был вызван.Из cppreference :
2) если T является типом класса, не являющимся объединением, без каких-либо пользовательских конструкторов, каждый нестатический член данных и компонент базового класса Tзначение инициализирован;(до C ++ 11)
В этом случае оба значения b.B::i
и b.B::A
будут инициализированы значением, что приведет к такому поведению, но это помечено как "(до C ++ 11) ".