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

Скрепляющий или равный-инициализатор в союзах

Связано: Как инициализировать член не-POD в Союзе

В стандарте говорится

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

Но

struct Point {
    Point() {}
    Point(int x, int y): x_(x), y_(y) {}
    int x_, y_;
};

union U {
    int z;
    double w;
    Point p = Point(1,2);
};


#include <iostream>
int main () {
    U u;
    std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
}

выводит 4196960:0 вместо ожидаемого 1:2.

Я считаю это ошибкой компилятора. Это так?

4b9b3361

Ответ 1

С++ 11 [class.ctor]/5 состояния:

Конструктор по умолчанию для класса X является конструктором класса X, который можно вызвать без аргумента. Если для класса X нет объявленного пользователем конструктора, конструктор, не имеющий параметров, неявно объявляется как дефолт (8.4). Неявно объявленный конструктор по умолчанию является членом inline public его класса. По умолчанию конструктор по умолчанию для класса X определяется как удаленный, если:

  • X - это унифицированный класс, имеющий вариантный вариант с нетривиальным конструктором по умолчанию,
  • любой нестатический элемент данных без элемента с выравниванием или равным значением имеет ссылочный тип,
  • любой невариантный нестатический член данных типа const-qual (или его массив) без элемента с выравниванием или равным-инициализатором не имеет предоставленного пользователем конструктора по умолчанию,
  • X является объединением, и все его члены варианта имеют const-квалифицированный тип (или его массив),
  • X - это неединичный класс, и все члены любого анонимного члена объединения имеют тип const (или его массив),
  • любой прямой или виртуальный базовый класс или нестатический элемент данных, не имеющий элементарного или равного-инициализатора, имеет тип класса M (или его массив), и либо M не имеет конструктора по умолчанию или разрешения перегрузки ( 13.3) применительно к конструктору по умолчанию M приводит к двусмысленности или функции, которая удалена или недоступна по умолчанию по умолчанию, или
  • любой прямой или виртуальный базовый класс или нестатический член данных имеет тип с деструктором, который удален или недоступен из стандартного конструктора по умолчанию.

Конструктор по умолчанию является тривиальным, если он не предоставляется пользователем, и если:

  • его класс не имеет виртуальных функций (10.3) и нет виртуальных базовых классов (10.1) и
  • нет нестатического элемента данных его класса, который имеет инициализатор скобок или равный, и
  • все прямые базовые классы его класса имеют тривиальные конструкторы по умолчанию, а
  • для всех нестатических членов данных своего класса, которые относятся к типу класса (или его массиву), каждый такой класс имеет тривиальный конструктор по умолчанию.

В противном случае конструктор по умолчанию является нетривиальным.

Так как struct Point в OP имеет нетривиальный конструктор по умолчанию,

Point() {}

используемый по умолчанию конструктор по умолчанию для объединения, содержащий член типа Point, должен быть определен как удаленный в соответствии с первым номером:

  • X - это унифицированный класс, который имеет вариантный член с нетривиальным конструктором по умолчанию

в результате чего программа, представленная в OP, плохо сформирована.

Однако комитет, похоже, считает это дефектом в случае, когда член союза имеет скользящий или равный инициализатор, за основной рабочей группы выпуск 1623:

Согласно 12.1 [class.ctor], пункт 5,

По умолчанию конструктор по умолчанию для класса X определяется как удаленный, если:

     
  •   
  • X - это унифицированный класс, который имеет вариантный член с нетривиальным конструктором по умолчанию,

      
  • ...

      
  • X является объединением, и все его элементы варианта имеют const-квалифицированный тип (или его массив),

      
  • X - это неединичный класс, и все члены любого анонимного члена объединения имеют тип const (или его массив),

      
  • ...

      

Поскольку наличие инициализатора нестатического элемента данных является моральным эквивалентом mem-initializer, эти правила, вероятно, должны быть изменены, чтобы не определять сгенерированный конструктор как удаленный, когда член объединения имеет инициализатор нестатического элемента данных, (Обратите внимание на ненормативные ссылки в параграфах пункта 2 раздела 9.5 [class.union] и 7.1.6.1 [dcl.type.cv], которые также необходимо будет обновить, если это ограничение будет изменено.)

Также было бы полезно добавить требование к 9.5 [class.union], требующее либо инициализатора нестатического элемента данных, либо предоставленного пользователем конструктора, если все члены объединения имеют const-квалифицированные типы.

В более общем примечании, почему конструктор по умолчанию, определенный как удаленный, только потому, что у элемента есть нетривиальный конструктор по умолчанию? Сам союз не знает, какой член является активным, а построение по умолчанию не будет инициализировать каких-либо членов (при условии, что он не имеет элементарного или равного-инициализатора). До "владельца" союза контролировать время жизни активного члена (если есть) и требовать, чтобы предоставленный пользователем конструктор заставлял шаблон проектирования, который не имеет смысла. Вдоль тех же строк, почему деструктор по умолчанию определяется как удаленный только потому, что у элемента есть нетривиальный деструктор? Я бы согласился с этим ограничением, если он применяется только тогда, когда союз также имеет предоставленный пользователем конструктор.

Проблема 1623 имеет статус "составления проекта", что указывает на то, что комитет считает, что проблема, вероятно, является дефектом, - почему еще разрешить выравниватель-равный-инициализатор для члена профсоюза? - но еще не выделил времени для определения правильной формулировки резолюции. Действительно, этот абзац во многом совпадает с текущим проектом С++ 14 N3936 ([class.ctor]/4), за исключением того, что формулировка "любой прямой или виртуальный базовый класс или нестатический элемент данных" везде заменяется более простой "любой потенциально сконструированный подобъект."

Хотя поведение обоих компиляторов не строго соответствует, я бы подумал, что Кланг ведет себя в духе стандарта. Похоже, что GCC запутается в комбинации удаленных конструкторов по умолчанию и вставных или равных-инициализаторов:

GCC, вероятно, должен либо соответствовать стандарту, либо диагностировать программу как плохо сформированную, либо эмулировать поведение clang, и генерировать правильный конструктор из элемента выравнивания или равного.