Подтвердить что ты не робот

Стек элемента-константы против кучи

Если я попытаюсь скомпилировать этот код

struct A {
    const int j;
};
A a;

Я получу ожидаемую ошибку:

error: uninitialized const member в 'struct A

но если я попытаюсь скомпилировать этот:

struct A {
    const int j;
};
A * a = new A();

Я получу успешную сборку.

Возникает вопрос: почему распределение new позволяет создавать переменную с константным элементом без явного конструктора и распределения стека - не?

4b9b3361

Ответ 1

Это не из-за выделения кучи, а из-за скобки, которую вы используете при распределении. Если вы делаете, например,

A* a = new A;

он также потерпит неудачу.

Причина, по которой она работает, когда вы добавляете скобки, состоит в том, что тогда ваша структура инициализируется значением, а для значения типа POD типа A инициализирует значение каждого элемента и инициализацию значения по умолчанию для int равна нулю.

Это означает, что, вероятно, это будет работать с созданием переменной в стеке, если вы просто добавите скобки для инициализации значения:

A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse

Хотя это приводит к другим потенциальным проблемам, лучше использовать единую инициализацию, если вы можете (требуется С++ 11):

A a{};

Ответ 2

Это плохо сформировано с С++ 14. §12.1 [class.ctor] говорит, что

4 По умолчанию конструктор по умолчанию для класса X определяется как удаленный если:

  • [...]
  • любой невариантный нестатический элемент данных типа const-qual (или его массив) без элемента с выравниванием или равным-инициализатором не имеет пользовательский конструктор по умолчанию,
  • [...]

Конструктор по умолчанию является тривиальным, если он не предоставляется пользователем, и если:

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

§8.5 [dcl.init] говорит, в свою очередь, что

7 Для инициализации объекта типа T по умолчанию:

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

8 Для инициализации объекта типа типа T:

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

Пустая пара скобок приводит к инициализации значения. Конструктор по умолчанию для A определяется как удаленный per [class.ctor]/p4. Таким образом, по [dcl.init]/p8 инициализация значения означает инициализацию по умолчанию, а через p7 инициализация плохо сформирована, потому что конструктор удален.

Версия С++ 11 фактически разрешила инициализацию инициализации в этом контексте; он говорит для инициализации значения, что, между прочим,

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

Так как по определению выше конструктор по умолчанию тривиален (хотя он и удален), он фактически не вызывается. Это считалось дефектом в стандарте, и соответствующая формулировка была изменена на CWG-выпуск 1301.

Поставщики компиляторов обычно реализуют разрешения дефектов, поэтому это можно считать ошибкой в ​​GCC 4.8, которая была исправлена ​​в 4.9.