2014-11-29 3 views
13

Если я пытаюсь скомпилировать this codeчлен Const стек против кучи

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

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

error: uninitialized const member in ‘struct A’

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

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

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

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

+1

Не компилируется с GCC 4.9 или Clang. Http: //coliru.stacked-crooked.com/a/fadd1a456bfd5b82 –

+0

@remyabel Что, очевидно, ничего не говорит о правильности этого кода. –

ответ

10

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

A* a = new A; 

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

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

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

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

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

A a{}; 
+6

'A a();' будет, конечно, * никогда * работать. –

+1

Да, это было бы объявлено как объявление функции. – ravi

+0

'A a = A();' будет работать, но работает 'A a {};' действительно работает? – juanchopanza

6

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

4 A defaulted default constructor for class X is defined as deleted if:

  • [...]
  • any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
  • [...]

A default constructor is trivial if it is not user-provided and if:

  • its class has no virtual functions (10.3) and no virtual base classes (10.1), and
  • no non-static data member of its class has a brace-or-equal-initializer, and
  • all the direct base classes of its class have trivial default constructors, and
  • for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.

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

7 To default-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an ambiguity or in a function that is deleted or inaccessible from the context of the initialization);
  • [...]

8 To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
  • [...]

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

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

if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.

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

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

Смежные вопросы