Почему
struct wrapper
{
explicit wrapper(void *);
wrapper() = default;
int v;
};
int main() { return wrapper().v; } // You should run this in Debug mode
вернуть 0xCCCCCCCC
, в то время как
struct wrapper { wrapper() = default; int v; };
int main() { return wrapper().v; }
а также
struct wrapper { int v; };
int main() { return wrapper().v; }
оба возвращаются 0
?
В течение Значение инициализация, если T
является типом класса без предоставленного пользователем или удаленного конструктора по умолчанию, тогда объект нулевой инициализируется (§8.5 / 8,2). Это действительно так с wrapper
,
Ваш первый пример соответствует третьему случаю для инициализации нуля (§8.5 / 6.1, выделение мое)
— если
T
является скалярным типом (3.9), объект инициализируется значением, полученным путем преобразования целочисленного литерала
0
(ноль) доT
;— если
T
является (возможно, квалифицированным по cv) типом несоединения, каждый нестатический член данных и каждый подобъект базового класса инициализируются нулями, а заполнение инициализируется нулевыми битами;— если
T
тип объединения (возможно, cv-квалифицированный), первый нестатический именованный элемент данных объекта инициализируется нулями, а заполнение инициализируется нулями;— если T является типом массива, каждый элемент инициализируется нулями
— если T является ссылочным типом, инициализация не выполняется
Итак, в вашем первом примере, v
должен быть инициализирован нулем. Это похоже на ошибку.
Во втором и третьем примере у вас больше нет предоставленного пользователем конструктора, но у вас есть конструктор по умолчанию, который не предоставлен или не удален пользователем, поэтому ваш пример все еще попадает в третий случай для инициализации нуля, который заключается в инициализировать нулями каждый нестатический элемент данных. ВС там правильно.
Это похоже на ошибку в MSVC. Во всех трех случаях wrapper
не имеет предоставленного пользователем конструктора по умолчанию, поэтому инициализация с wrapper()
вызывает:
(Все цитаты из n3690)
(8.5 / 11) Объект, инициализатором которого является пустой набор скобок, т.е. (), должен быть инициализирован значением.
(благодаря dyp), это приведет к нулевой инициализации int v
Затем инициализация отсылает нас к правилу:
(8.5 / 8) если T является (возможно, cv-квалифицированным) типом класса без предоставленного пользователем или удаленного конструктора по умолчанию, тогда объект инициализируется нулями и проверяются семантические ограничения для инициализации по умолчанию.
Состояние правил нулевой инициализации:
(8.5 / 6) если T является (возможно, квалифицированным по cv) типом класса, не являющимся объединением, каждый нестатический член данных и каждый подобъект базового класса инициализируются нулями, а заполнение инициализируется нулевыми битами
int v
будучи членом данных wrapper
инициализируется нулем в соответствии с:
(8.5 / 6) если T является скалярным типом (3.9), объект инициализируется значением, полученным путем преобразования целочисленного литерала 0 (нуля) в T
Который не поведение, которое вы наблюдаете.