дефолтные различия ctor между gcc 4.6 и 4.7

В GCC 4.6.1, когда я объявляю экземпляр моего собственного типа, который имеет конструктор по умолчанию, и если я создаю экземпляр объекта этого типа и инициализирую его фигурными скобками (например, Foo my_foo {};), члены POD в этом классе будет инициализироваться нулем только в том случае, если не объявлен другой конструктор. Если нет другого конструктора, кроме дефолтного, они будут инициализироваться с нуля, как и ожидалось.

Но в GCC 4.7.3 инициализация нуля происходит в любом случае, что я и ожидал.

Какая здесь разница? Это ошибка компилятора? Обе эти версии GCC поддерживают конструкторы по умолчанию стандарта C ++ 11.

Нет никакой необходимости придерживаться старых версий GCC, но я бы хотел понять, что здесь происходит.

примечание: я по умолчанию основной ctor, op =. и скопируйте ctor просто для того, чтобы сохранить тип пригодным для использования с переменными функциями (clang требует, чтобы класс был классифицирован как POD, хотя gcc позволяет мне избежать использования типа с переменными функциями даже с определяемыми пользователем основными ctor. бонусные баллы, если вы можете сказать, мне почему.)

Вот пример программы для иллюстрации, включая некоторые выходные данные внизу (из двоичных файлов, скомпилированных с обеими версиями GCC):

#include <cstdio>

// pod and pod_wctor are identical except that pod_wctor defines another ctor

struct pod {
pod( void ) = default;
pod( const pod& other ) = default;
pod& operator=( const pod& other ) = default;

int x,y,z;
};

struct pod_wctor {
pod_wctor( void ) = default;
pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { }
pod_wctor( const pod_wctor& other ) = default;
pod_wctor& operator=( const pod_wctor& other ) = default;

int x,y,z;
};

int main ( void ) {

printf("the following shuold be uninitialized:\n");

pod pee;
printf( "    %i,%i,%i\n", pee.x, pee.y, pee.z);

pod_wctor podtor;
printf( "    %i,%i,%i\n", podtor.x, podtor.y, podtor.z);

printf("the following shuold be initialized to 0,0,0:\n");

pod peenit{};
printf( "    %i,%i,%i\n", peenit.x, peenit.y, peenit.z );

pod_wctor podtornit{};
printf( "    %i,%i,%i\n", podtornit.x, podtornit.y, podtornit.z );

return 0;

}

// compiled with: g++ m.cpp -std=gnu++0x
// g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 (i386)
/****************** output *******************
the following shuold be uninitialized:
10381592,134513249,134520820
134513969,134513504,0
the following shuold be initialized to 0,0,0:
0,0,0
7367877,134513945,8724468
*********************************************/

// compiled with: g++ m.cpp -std=gnu++0x
// gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-2ubuntu4) (i386)
/****************** output *******************
the following shuold be uninitialized:
-1218358300,-1217268232,134520832
134514450,1,-1079827548
the following shuold be initialized to 0,0,0:
0,0,0
0,0,0
*********************************************/

6

Решение

Добавив конструктор pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { } для вашего класса он теряет свой статус как агрегат: [dcl.init.aggregate] / 1

Агрегат — это массив или класс (раздел 9) без пользовательских конструкторов

Это все еще POD, потому что тривиальный класс нужно только не иметь нетривиальных дефолт ctors: [класс] / 6

тривиальный класс является классом, который имеет конструктор по умолчанию (12.1), не имеет нетривиальных конструкторов по умолчанию,
и тривиально копируемый.


Интересным моментом здесь является то, что для агрегата, инициализация списка pod peenit{}; выполняет агрегатную инициализацию:

Инициализация списка объекта или ссылки типа T определяется следующим образом:

  • Если T является агрегатом, выполняется агрегатная инициализация (8.5.1). […]
  • В противном случае, если список инициализатора не имеет элементов и T тип класса с конструктором по умолчанию, объект инициализируется значением

(Примечание: это пересмотренный порядок. AFAIK, в самом Стандарте, порядок этих двух точек обращен, что должно быть дефектом, так как у каждого агрегата есть ctor по умолчанию — неявно объявленный&определил один.)

Агрегированная инициализация приводит к инициализации значения int участники: [dcl.init.aggr] / 7

Если есть меньше инициализатора-статьи в списке, чем есть элементы в совокупности, то каждый элемент, не инициализированный явно, должен быть инициализирован из пустого списка инициализатора

и [dcl.init.list] / 3 «В противном случае, если список инициализаторов не имеет элементов, объект инициализируется значением»


Тем не менее, для неагрегированных pod_wctor, список-инициализация pod_wctor podtornit{} непосредственно выполняет инициализацию значения, которая вызывает ctor по умолчанию. [class.ctor] / 6 указывает:

Неявно определенный конструктор по умолчанию выполняет набор инициализаций класса, который будет выполнен пользовательским конструктором по умолчанию для этого класса без т е р-инициализатор (12.6.2) и пустой компаунд-заявление.

и в [class.base.init] / 8 мы находим:

В не делегирующем конструкторе, если данный нестатический элемент данных или базовый класс не обозначен мем-инициализатор-идентификатор (включая случай, когда нет мем-инициализатора-лист потому что конструктор не имеет т е р-инициализатор) и сущность не является виртуальным базовым классом абстрактного класса (10.4), то

  • […]
  • в противном случае объект инициализируется по умолчанию (8.5).

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


Разница между инициализацией по умолчанию и значением: [dcl.init]

[7] Для по умолчанию инициализирует объект типа T средства:

  • если T это (возможно резюме-квалифицированный) тип класса, конструктор по умолчанию для T называется […]
  • […]
  • в противном случае инициализация не выполняется.
[…] [8] Для Значение инициализации объект типа T средства:

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

(Я признаю, что это смутило меня, и мне пришлось пересмотреть мой ответ.)

pod_wctor имеет конструктор по умолчанию, который не предоставленный пользователем. Поэтому для инициализации списка pod_wctor podtornit{}вторая пуля Значение инициализация применяется. Предмет podtornit сам по себе нулевой инициализируется, что приводит к нулевой инициализации его членов. Только затем будет ли он инициализирован по умолчанию, и будет вызываться ctor по умолчанию. Последний ничего не делает, но первый гарантирует, что члены будут обнулены.

2

Другие решения

Других решений пока нет …