Как вызвать clang ++ или g ++ для точного копирования требований в двух разных стандартных версиях

Я пытаюсь зафиксировать различия между 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';
}

следующее:

  1. Согласно N3337, конструктор по умолчанию для Derived называется, как Derived имеет предоставленный пользователем конструктор. Конструктор по умолчанию для Derived звонки Base конструктор по умолчанию, который инициализирует Derived::i = 10, оставляя Derived::j инициализированы.
  2. От N3797, а Derived не имеет ни предоставленного пользователем конструктора по умолчанию, ни удаленного конструктора по умолчанию, применяется вторая точка маркера. То есть, Derived инициализируется нулями, т. е. оба Derived::i а также Derived::j инициализируются 0 и объектом d инициализируется по умолчанию, что оставляет Derived::i = 10,

Хотя мои знания Unixes минимальны, я пытался воспроизвести эти два случая, используя разные флаги для компиляторов clang ++ и g ++ методом проб и ошибок в Coliru, но безрезультатно. Результаты пока что все напечатаны d.i = 10 d.j = 0 без предупреждения.

4

Решение

Программа в ОП не может различить, если 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) демонстрирует:

Таким образом, нет способа заставить компилятор выполнить именно так как указано, но мы обычно можем определить, что происходит в любом случае с некоторым тщательным анализом.

3

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

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

По вопросам рекламы [email protected]