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

Member not zeroed, ошибка clang++?

Рассмотрим следующий код:

class A {
public:
    int i;
    A() {}
};

class B {
public:
    A a;
    int i;
};

int main() {
    B* p = new B {};
    std::cout << p->i << " " << p->a.i << "\n";
}

Скомпилированный с -std = С++ 11 в clang++, p->i оказывается равным нулю, но p->a.i этого не делает. Не следует ли обнулить весь объект до тех пор, пока его класс не имеет конструктора, предоставленного пользователем?

EDIT: Поскольку в комментариях есть несколько подробных обсуждений, я думаю, что лучше добавить некоторые выдержки из стандарта здесь:

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

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

Для нулевой инициализации объекта или ссылки типа T означает:

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

Здесь применяется вторая пуля.

4b9b3361

Ответ 1

Clang правильный, по стандарту С++ 11 плюс соответствующие DR

В исходной спецификации С++ 11 B{} выполнит инициализацию значения, в результате чего a.i будет инициализироваться нулем. Это было изменение поведения по сравнению с С++ 98 для таких случаев, как

B b = {};

... которые обрабатывались как агрегатная инициализация в С++ 98, но рассматривались как инициализация значений в С++ 11 FDIS.

Однако поведение в этом случае было изменено с помощью основной проблемы 1301, которая восстановила поведение С++ 98, указав, что агрегатная инициализация используется всякий раз, когда агрегат инициализируется скобками-init-list. Поскольку эта проблема считается DR, она рассматривается как де-факто применительно к более ранним версиям стандарта С++, поэтому ожидается, что соответствующий компилятор С++ 11 будет выполнять агрегатную инициализацию, а не инициализацию значения.

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

Ответ 2

Он действительно выглядит как ошибка (или, как указано в комментариях, ведет себя в соответствии с С++ 03, несмотря на указание С++ 11). В С++ 11 инициализация значения должна обнулять члены a перед вызовом его конструктора по умолчанию. Инициализация B определяется этим правилом 8.5/7

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

Нулевая инициализация должна рекурсивно инициализировать нуль a за это правило 8.5/5

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

и, конечно, нулевая инициализация a должна установить i в ноль.

Ответ 3

Это not ошибка компилятора , это ошибка в коде. Компилятор, похоже, реализует поведение С++ 03, но это сильно изменилось в С++ 11.

Это некоторые релевантные цитаты из стандартов С++ 03 и С++ 11

В С++ 03:

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

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

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

(акцент мой)

В С++ 11:

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

- если T является (возможно, cv-qualified) тип класса (раздел 9) с предоставленным пользователем конструктором (12.1), то конструктор по умолчанию для T называется (и инициализация плохо сформирована, если T не имеет доступного значения по умолчанию конструктор);

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

и

Для нулевой инициализации объекта или ссылки типа T означает:

- если T является скалярного типа (3.9), объект устанавливается в значение 0 (ноль), взятое как интегральное постоянное выражение, преобразованное в T;

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

Примечание. Следующее относится только к С++ 03:

Либо удалите A созданный пользователем конструктор, либо измените его на

A() : i() {}

Когда вы стоите-инициализируете B здесь,

B* p = new B {};

it value инициализирует свои члены данных. Поскольку A имеет конструктор по умолчанию, инициализация значения приводит к вызову. Но этот конструктор явно не инициализирует A::i, поэтому он инициализируется по умолчанию, что для int означает, что инициализация не выполняется.

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

Ответ 4

Интегральные типы не должны быть инициализированы значением, подобным этому в конструкторе, отличном от стандартного (поскольку вы предоставили конструктор)

Измените свой конструктор на A() : i(0) {}.