инициализатор в виде фигурных скобок или равенства в объединениях
-
20-12-2019 - |
Вопрос
Связанный: Как инициализировать не-POD-член в Union
В стандарте говорится
Не более чем один нестатический элемент данных объединения может иметь инициализатор в виде фигурных скобок или равенства.
Но
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
.
Я считаю это ошибкой компилятора.Это так?
Решение
C++11 [class.ctor]/5 состояний:
A по умолчанию конструктор для класса
X
является конструктором классаX
это может быть вызвано без аргумента.Если для класса нет объявленного пользователем конструктораX
, конструктор, не имеющий параметров, неявно объявляется как defaulted (8.4).Неявно объявленный конструктор по умолчанию - этоinline public
представитель своего класса.Установленный по умолчанию конструктор для классаX
определяется как удаленный, если:
X
это класс, подобный объединению, который имеет элемент variant с нетривиальным конструктором по умолчанию,- любой нестатический элемент данных без инициализатор с фигурной скобкой или равным значением имеет ссылочный тип,
- любой невариантный нестатический элемент данных типа const-qualified (или его массива) без инициализатор с фигурной скобкой или равным значением не имеет предоставляемого пользователем конструктора по умолчанию,
X
является объединением, и все его вариантные члены имеют тип const-qualified (или его массив),X
является классом, не являющимся объединением, и все члены любого анонимного члена объединения имеют тип const-qualified (или его массив),- любой прямой или виртуальный базовый класс, или нестатический элемент данных без инициализатор с фигурной скобкой или равным значением, имеет тип класса
M
(или их массив) и либоM
не имеет конструктора по умолчанию или разрешения перегрузки (13.3) применительно кM
конструктор по умолчанию приводит к неоднозначности или к функции, которая удалена или недоступна из конструктора по умолчанию, или- любой прямой или виртуальный базовый класс или нестатический элемент данных имеет тип с деструктором, который удален или недоступен из конструктора по умолчанию.
Конструктор по умолчанию является тривиальным, если он не предоставляется пользователем и если:
- его класс не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1), и
- ни один нестатический элемент данных своего класса не имеет инициализатор с фигурной скобкой или равным значением, и
- все прямые базовые классы его класса имеют тривиальные конструкторы по умолчанию, и
- для всех нестатических элементов данных своего класса, которые относятся к типу class (или его массиву), каждый такой класс имеет тривиальный конструктор по умолчанию.
В противном случае конструктором по умолчанию является нетривиальный.
Поскольку структура Point
в OP есть нетривиальный конструктор по умолчанию,
Point() {}
конструктор по умолчанию, используемый по умолчанию для объединения, содержащего элемент типа Point
должен быть определено как удаленное в соответствии с первым маркером:
X
это класс, подобный объединению, который имеет элемент variant с нетривиальным конструктором по умолчанию
в результате программа, представленная в ОП, была неправильно сформирована.
Однако комитет, по-видимому, считает это недостатком в том случае, если у члена профсоюза есть инициализатор с фигурной скобкой или равным значением, за проблема основной рабочей группы 1623:
В соответствии с пунктом 5 статьи 12.1 [class.ctor],
Конструктор по умолчанию для класса X определяется как удаленный, если:
X
это класс, подобный объединению, который имеет элемент variant с нетривиальным конструктором по умолчанию,...
X
является объединением, и все его вариантные члены имеют тип const-qualified (или его массив),
X
является классом, не являющимся объединением, и все члены любого анонимного члена объединения имеют тип const-qualified (или его массив),...
Поскольку наличие нестатического инициализатора элемента данных является моральным эквивалентом mem-инициализатор, эти правила, вероятно, следует изменить, чтобы не определять сгенерированный конструктор как удаленный, когда член объединения имеет нестатический инициализатор элемента данных.(Обратите внимание на ненормативные ссылки в пунктах 2-3 пункта 9.5 [class.union] и пункте 2 пункта 7.1.6.1 [dcl.type.cv], которые также потребуется обновить, если это ограничение будет изменено.)
Также было бы полезно добавить требование к 9.5 [class.union], требующее либо нестатического инициализатора элемента данных, либо предоставляемого пользователем конструктора, если все члены объединения имеют типы с константной квалификацией.
В более общем плане, почему конструктор по умолчанию определен как удаленный только потому, что у элемента есть нетривиальный конструктор по умолчанию?Само объединение не знает, какой член является активным, и конструкция по умолчанию не будет инициализировать никаких членов (при условии, что нет инициализатор с фигурной скобкой или равным значением).“Владелец” объединения должен контролировать время жизни активного члена (если таковой имеется), и требование наличия предоставляемого пользователем конструктора приводит к созданию шаблона проектирования, который не имеет смысла.Аналогично, почему деструктор по умолчанию определен как удаленный только потому, что у элемента есть нетривиальный деструктор?Я бы согласился с этим ограничением, если бы оно применялось только в том случае, если у объединения также есть предоставляемый пользователем конструктор.
Выпуск 1623 имеет статус "редакционный", что указывает на то, что комитет считает, что проблема, вероятно, является дефектом - зачем еще допускать инициализатор с фигурной скобкой или равным значением для члена профсоюза?- но еще не уделил времени тому, чтобы определить правильную формулировку резолюции.Действительно, этот абзац в основном такой же, как в текущем проекте C++14 N3936 ([class.ctor]/4), за исключением того, что формулировка "любой прямой или виртуальный базовый класс или нестатический элемент данных" везде заменена более простым "любой потенциально сконструированный подобъект".
Хотя поведение обоих компиляторов не является строго соответствующим, я бы счел, что Clang ведет себя в духе стандарта.Может показаться, что GCC приходит в замешательство из-за сочетания удаленного конструктора по умолчанию и инициализатор с фигурной скобкой или равным значением:
- Оно делает диагностировать программу как некорректно сформированную при отсутствии инициализатор с фигурной скобкой или равным значением,
с инициализатор с фигурной скобкой или равным значением текущие и максимальные предупреждения GCC 4.8.2 вообще не выполняет инициализацию объединения, и даже предупреждает, что члены используются неинициализированными:
main.cpp: In function 'int main()': main.cpp:17:39: warning: 'u.U::p.Point::y_' is used uninitialized in this function [-Wuninitialized] std::cout << u.p.x_ << ":" << u.p.y_ << std::endl; ^ main.cpp:17:22: warning: 'u.U::p.Point::x_' is used uninitialized in this function [-Wuninitialized] std::cout << u.p.x_ << ":" << u.p.y_ << std::endl; ^
GCC, вероятно, должен либо соответствовать стандарту и диагностировать программу как некорректно сформированную, либо эмулировать поведение clang и сгенерировать соответствующий конструктор из инициализатор с фигурной скобкой или равным значением.