Я пытаюсь зафиксировать различия между N3337 §8.5p7 (C ++ 11) и N3797 §8.5p8 (пост C ++ 11), которые имеют дело с инициализацией значения.
N3337 §8.5p7:
Инициализировать значение объекта типа T означает:
- если T является (возможно, cv-квалифицированным) типом класса (раздел 9) с предоставленным пользователем конструктором (12.1), тогда конструктор по умолчанию для T
называется (и инициализация плохо сформирована, если T не имеет доступных
конструктор по умолчанию);- если T является (возможно, cv-квалифицированным) типом класса, не являющимся объединением, без предоставленного пользователем конструктора, то объект инициализируется нулями и, если
Неявно объявленный конструктор T по умолчанию является нетривиальным, что
конструктор называется.- если T является типом массива, то каждый элемент инициализируется значением;
- в противном случае объект инициализируется нулями.
Объект, инициализированный значением, считается созданным и, следовательно, подлежит
положения настоящего международного стандарта, применяемые к «построенным»
объекты, объекты, «для которых конструктор выполнил» и т. д., даже
если конструктор не вызывается для инициализации объекта.
N3797 §8.5p8:
Инициализировать значение объекта типа T означает:
- если T является (возможно, cv-квалифицированным) типом класса (раздел 9) без конструктора по умолчанию (12.1) или конструктора по умолчанию, который
предоставляется пользователем или удаляется, затем объект инициализируется по умолчанию;- если T является (возможно, cv-квалифицированным) типом класса без предоставленного пользователем или удаленного конструктора по умолчанию, тогда объект инициализируется нулями
и семантические ограничения для инициализации по умолчанию проверяются,
и если T имеет нетривиальный конструктор по умолчанию, объект
по умолчанию инициализируется;- если T является типом массива, то каждый элемент инициализируется значением;
- в противном случае объект инициализируется нулями.
Объект, инициализированный значением, считается созданным и
таким образом, с учетом положений настоящего международного стандарта, применяемого к
«Построенные» объекты, объекты «для которых конструктор имеет
завершено »и т. д., даже если для объекта не вызывается конструктор
инициализация.
Учитывая эти два правила, приведенный ниже фрагмент кода должен дать разные результаты:
#include <iostream>
struct Base {
int i;
Base(int i):i(i) {}
Base():i(10) {}
};
struct Derived : public Base {
int j;
Derived(int j):Base(j), j(j) {}
Derived()=default;
};
int main() {
Derived d{};
std::cout << "d.i = " << d.i << " " << "d.j = " << d.j << '\n';
}
следующее:
Derived
называется, как Derived
имеет предоставленный пользователем конструктор. Конструктор по умолчанию для Derived
звонки Base
конструктор по умолчанию, который инициализирует Derived::i = 10
, оставляя Derived::j
инициализированы.Derived
не имеет ни предоставленного пользователем конструктора по умолчанию, ни удаленного конструктора по умолчанию, применяется вторая точка маркера. То есть, Derived
инициализируется нулями, т. е. оба Derived::i
а также Derived::j
инициализируются 0 и объектом d
инициализируется по умолчанию, что оставляет Derived::i = 10
,Хотя мои знания Unixes минимальны, я пытался воспроизвести эти два случая, используя разные флаги для компиляторов clang ++ и g ++ методом проб и ошибок в Coliru, но безрезультатно. Результаты пока что все напечатаны d.i = 10 d.j = 0
без предупреждения.
Программа в ОП не может различить, если d.j
инициализируется в 0 или если он неинициализирован и по совпадению оказывается равным 0. Это было бы понятно, если бы рассматриваемый производный объект был создан в памяти, которая уже инициализирована с известным ненулевым значением, скажем с размещением new:
Derived d{42}; // d.i and d.j are both 42.
::new (&d) Derived{}; // d.i is 0, d.j is 0 per N3797 or 42 per N3337.
Как Дип говорит в своем комментарии, Компиляторы обычно отслеживают изменения из-за дефектов в стандарте (в отличие от новых функций) и включают их в свою поддержку конкретной редакции стандарта. Вероятно, нет компилятора, который компилирует именно так язык, указанный в любом данном стандартном документе, учитывая, что стандарты постоянно меняются. Когда вы говорите, например, clang 3.4, чтобы скомпилировать C ++ 11, язык, который он фактически реализует, является «частью C ++ 11 плюс соответствующие исправления дефектов, которые мы реализовали (IIRC все это для 3.4) ко времени выпуска 3.4» «.
Конкретное изменение в Значение инициализация формулировка, о которой спрашивает ФП, произошла в резолюции Отчет о дефектах (DR) основной рабочей группы (CWG) № 1301 который также адресован DR1324 а также DR1368. В качестве устранения дефекта у компиляторов будет тогда причина для внесения изменений.
Анализ с различными компиляторами и версиями (в основном выполняется OP) демонстрирует:
Таким образом, нет способа заставить компилятор выполнить именно так как указано, но мы обычно можем определить, что происходит в любом случае с некоторым тщательным анализом.
Других решений пока нет …