Всегда ли конструктор по умолчанию инициализирует все элементы?

Я могу поклясться, что не помню, что видел это раньше, и мне трудно поверить своим глазам:

Инициализирует ли неявно определенный конструктор по умолчанию для неагрегированного класса свои члены или нет?

В Visual C++, когда я запускаю этот невинно выглядящий код...

#include <string>
struct S { int a; std::string b; };
int main() { return S().a; }

... к моему удивлению, он возвращает ненулевое значение! Но если я уберу поле b, то он возвращает ноль.

Я пробовал это на всех версиях VC++, я могу получить в свои руки, и, похоже, делает это на всех из них.

Но когда я пробую это на Clang и GCC, значения инициализируются нулями, независимо от того, пробую ли я это в режиме C++98 или C++11.

Какое правильное поведение? Разве это не гарантировано быть нулем?

2 ответа

Решение

Цитирую C++11:

5.2.3 Явное преобразование типов (функциональная запись) [expr.type.conv]

2 Выражение T(), где T является спецификатором простого типа или спецификатором типа для полного типа объекта, не являющегося массивом, или (возможно, cv-квалифицированного) void type, создает значение указанного типа, которое инициализируется значением (8.5; инициализация для void() дело). [...]

8.5 Инициализаторы [dcl.init]

7 Инициализировать значение объекта типа T средства:

  • ...
  • если T является (возможно, квалифицированным по cv) типом класса без объединения без предоставленного пользователем конструктора, тогда объект инициализируется нулями и, если T Неявно объявленный конструктор по умолчанию нетривиален, этот конструктор вызывается.
  • ...

Так что в C++11 S().a должно быть равно нулю: объект инициализируется нулями до вызова конструктора, и конструктор никогда не изменяет значение a к чему-либо еще.

До C++11 инициализация значения имела другое описание. Цитата N1577 (примерно C++03):

Инициализировать значение объекта типа T означает:

  • ...
  • если T это тип класса без объединения без объявленного пользователем конструктора, тогда каждый нестатический член данных и компонент базового класса T инициализируется значением;
  • ...
  • в противном случае объект инициализируется нулями

Здесь значение инициализации S не вызывал конструктор, но вызывал инициализацию значения его a а также b члены. Значение инициализации этого a Таким образом, member вызвал нулевую инициализацию этого конкретного члена. В C++ 03 результат также гарантированно равен нулю.

Еще раньше, переходя к самому первому стандарту, C++98:

Выражение T(), где T является спецификатором простого типа (7.1.5.2) для завершенного типа объекта, не являющегося массивом, или (возможно, cv-квалифицированного) void type, создает значение r указанного типа, значение которого определяется default-initialization (8.5; инициализация для void() дело).

По умолчанию инициализировать объект типа T средства:

  • если T это тип класса не POD (раздел 9), конструктор по умолчанию для T называется (и инициализация плохо сформирована, если T не имеет доступного конструктора по умолчанию);
  • ...
  • в противном случае хранилище для объекта инициализируется нулями.

Поэтому, основываясь на этом самом первом стандарте, VC++ верен: когда вы добавляете std::string член, S становится не-POD-типом, а не-POD-типы не получают нулевую инициализацию, им просто вызывается их конструктор. Неявно созданный конструктор по умолчанию для S не инициализирует a член.

Таким образом, можно сказать, что все компиляторы верны, просто следуя различным версиям стандарта.

Как сообщает @Columbo в комментариях, более поздние версии VC++ вызывают a инициализируемый элемент в соответствии с более поздними версиями стандарта C++.

(Все цитаты в первом разделе взяты из N3337, C++11 FD с редакционными изменениями)

Я не могу воспроизвести поведение с VC++ на rextester. Предположительно, ошибка (см. Ниже) уже исправлена ​​в используемой ими версии, но не в вашей - @Drop сообщает, что последний выпуск, VS 2013 Update 4, не соответствует утверждению - в то время как предварительный просмотр VS 2015 передает их.

Просто чтобы избежать недоразумений: S это действительно совокупность. [Dcl.init.aggr]/1:

Агрегат - это массив или класс (раздел 9) без предоставленных пользователем конструкторов (12.1), без закрытых или защищенных нестатических элементов данных (пункт 11), без базовых классов (пункт 10) и без виртуальных функций (10.3).

Это не имеет значения, хотя.
Семантика инициализации значения важна. [Dcl.init]/11:

Объект, инициализатором которого является пустой набор скобок, т. Е. (), должен быть инициализирован значением.

[Dcl.init]/8:

Инициализировать значение объекта типа T средства:

  • если T является (возможно, cv-квалифицированным) типом класса (раздел 9) без конструктора по умолчанию (12.1) или конструктора по умолчанию, предоставленного или удаленного пользователем, тогда объект инициализируется по умолчанию;
  • если T является (возможно, cv-квалифицированным) типом класса без предоставленного пользователем или удаленного конструктора по умолчанию, тогда объект инициализируется нулями и проверяются семантические ограничения для инициализации по умолчанию, и если T имеет нетривиальный конструктор по умолчанию, объект инициализируется по умолчанию;
  • [..]

Очевидно, что это справедливо независимо от того, b в S или нет. Так по крайней мере в C++ 11 в обоих случаях a должно быть ноль. Clang и GCC показывают правильное поведение.


А теперь давайте посмотрим на C++03 FD:

Инициализировать значение объекта типа T средства:

  • если T тип класса (раздел 9) с конструктором, объявленным пользователем (12.1) [..]
  • если T это тип класса без объединения без объявленного пользователем конструктора, тогда каждый нестатический член данных и компонент базового класса T инициализируется значением;
  • если T тип массива, тогда каждый элемент инициализируется значением;
  • в противном случае объект инициализируется нулями

То есть даже в C++03 (где приведенная выше цитата в [dcl.init]/11 также существует в /7), a должно быть 0 в обоих случаях.
Опять же, и GCC, и Clang верны с -std= C++03.

Как показано в ответе hvd, ваша версия совместима только с C++98 и C++98.

Другие вопросы по тегам