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

Const T {}; работы, const T; терпит неудачу, когда T является не-POD,

Для начала у меня есть структура с одним значением со значением по умолчанию

struct S {
    int a = 1;
};

Этот тип может быть сконфигурирован по умолчанию, если он не const/non-constexpr как gcc, так и clang. Под обоими параметрами std::is_pod<S>::value есть false. Странное поведение выглядит следующим образом:

S s1; // works under both
const S s2{}; // works under both
const S s3; // only works in gcc, clang wants a user-provided constructor

Ни одна из следующих попыток не имеет значения для clang:

struct S {
    int a = 1;
    constexpr S() = default; // defaulted ctor
    virtual void f() { } // virtual function, not an aggregate
  private:
    int b = 2; // private member, really not an aggregate
};

Единственное, что я могу сделать, это сделать эту работу - добавить constexpr S() { } явно. Мне кажется совершенно неправильным, что const S s; выходит из строя при const S s{};, особенно когда тип не является агрегатом.

Стандарт заставляет меня думать, что Кланг прав
N4296: 8.5/7

Если программа вызывает инициализацию по умолчанию объекта const-квалифицированный тип T, T должен быть типом класса с предоставленным пользователем конструктор по умолчанию

Итак, почему gcc разрешает это и S{}; не инициализируется по умолчанию, даже если тип не является POD или агрегатом?

4b9b3361

Ответ 1

const S s3;

Подпадает под действие [dcl.init]/12:

Если для объекта не задан инициализатор, объект инициализируется по умолчанию.

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

struct S {
    int a = 1;
    constexpr S(){}
};

тогда делает компиляцию объявления.

[..] особенно, когда тип не является совокупностью.

S - это совокупность в вашем случае, и причина, по которой const S s{} действительна. Агрегатная инициализация применяется для const S s{}, и все отлично.
Если S не является совокупностью,

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

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

Теперь рассмотрим определение инициализации значения:

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

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

Значение по умолчанию ctor действительно нетривиально, так как элемент имеет инициализатор ([class.ctor]/4.9), но это не имеет значения, поскольку ограничения проверяются в любом случае. Следовательно, инициализация по умолчанию - это, а строка

const S s{};

Точно так же (или недействительно!) как

const S t;

Итак, почему gcc разрешает это

Ну:

  • Говоря о существующих стандартах, GCC не соответствует требованиям; См. Выше.

  • Существует активная проблема CWG, # 253, созданная пятнадцать лет назад, которая охватывает аналогичный сценарий. В заключительной записке об этом из собрания 2011 года говорится:

    Если неявный конструктор по умолчанию инициализирует все подобъекты, не требуется инициализатор.

    Это относится к неявному конструктору по умолчанию для S, и это сделает все ваши строки действительными.

  • Разработчики GCC (например, здесь) подразумевали, что, поскольку комитет в основном согласился с этой резолюцией, текущее поведение GCC возможен и не должен корректироваться. Поэтому можно утверждать, что GCC прав, и стандарт нарушен.

Ответ 2

Итак, похоже, что gcc основывает это на DR 253, хотя это еще не разрешено. Мы можем видеть это из следующего отчета gcc bug, в котором говорится:

Это по дизайну, потому что, как показывает DR 253, нормативный стандарт ошибочен.

и gcc change, которое ввело это в действие, говорит:

Core 234 - разрешить объекты const без инициализатора или     предоставленный пользователем конструктор по умолчанию, если конструктор по умолчанию     инициализирует все подобъекты.

Так технически clang правильный, а gcc не соответствует, но кажется, что они верят, что DR 253 будет разрешено в их пользу. Это имеет смысл, если основной проблемой является неопределенное начальное значение, которое, насколько я могу судить, есть. Это изменение задокументировано в gcc 4.6 примечания к выпуску:

В 4.6.0 и 4.6.1 g++ больше не разрешены объекты const-qual тип, который будет инициализироваться по умолчанию, если только тип не объявлен пользователем конструктор по умолчанию. В 4.6.2 g++ реализуется предлагаемая резолюция DR 253, поэтому инициализация по умолчанию разрешена, если она инициализирует все подобъектов. Код, который не удается скомпилировать, может быть исправлен путем предоставления инициализатор, например.

struct A { A(); };
struct B : A { int i; };
const B b = B();