Почему "ctor() = default" изменяет поведение при наличии других конструкторов?
Почему
struct wrapper
{
explicit wrapper(void *);
wrapper() = default;
int v;
};
int main() { return wrapper().v; } // You should run this in Debug mode
return 0xCCCCCCCC
, тогда как
struct wrapper { wrapper() = default; int v; };
int main() { return wrapper().v; }
и
struct wrapper { int v; };
int main() { return wrapper().v; }
оба возвращают 0
?
Ответы
Ответ 1
Во время инициализации значения, если T
- это тип класса без предоставленного пользователем или удаленного конструктора по умолчанию, тогда объект инициализируется нулем (§8.5/8.2). Это действительно так с wrapper
.
Ваш первый пример соответствует третьему случаю для нулевой инициализации (§8.5/6.1, акцент мой)
- если T
является скалярным типом (3.9), объект инициализируется значением, полученным преобразованием целочисленного литерала 0
(ноль) до T
;
- , если T
является (возможно, cv-квалифицированным) классом неединичного класса, каждый нестатический элемент данных и каждый под-объект базового класса инициализируются нулями, а заполнение инициализируется нулевыми битами;
- если T
является (возможно, cv-квалифицированным) типом объединения, объекты первого нестатического именованного элемента данных инициализируются нулями и заполнение инициализируется нулевыми битами;
- если T - тип массива, каждый элемент инициализируется нулем
- если T является ссылочным типом, инициализация не выполняется
Итак, в вашем первом примере v
должен быть инициализирован нулем. Это выглядит как ошибка.
В вашем втором и третьем примере у вас больше нет конструктора, предоставляемого пользователем, но у вас есть конструктор по умолчанию, который не предоставляется пользователем или не удаляется, поэтому ваш пример по-прежнему попадает в третий случай для нулевой инициализации, который является нулевой инициализацией каждого нестатического элемента данных. VS там верный.
Ответ 2
Это похоже на ошибку в 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
Это не поведение, которое вы наблюдаете.