Это касается конкретно 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 является типом класса с конструктором по умолчанию, объект инициализируется значением strong >.
- В противном случае, если 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-квалифицированным) типом класса без объединения без конструктора, предоставленного пользователем, то объект равен нулю -initialized и, если неявно объявленный конструктор 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)".
A() = default;
Или просто не определяйте его вообще, поскольку у вас нет членов пользовательских типов. - person Koori   schedule 03.01.2019b
получает нулевую инициализацию. Это должно обнулить все базовые и нестатические члены. Если только он не инициализируется нулем ... - person Mike Lui   schedule 03.01.2019A(){}
или изменить его наA() = default;
- person Koori   schedule 03.01.2019