Ответ 1
Объединение одного члена создает все виды проблем при попытке быть конструируемым по умолчанию и быть совместимым с constexpr.
Рассмотрите этот код:
struct nontrivial {
constexpr nontrivial(int o) : u{o} {}
int u;
};
union storage {
nontrivial nt;
};
struct optional {
storage s;
};
constexpr auto run() -> int {
optional o;
return o.s.nt.u;
}
int main() {
constexpr int t = run();
}
Это плохо сформировано, потому что optional
имеет удаленный конструктор.
Тогда простым исправлением будет добавление конструктора, который не инициализирует член объединения:
union storage {
constexpr storage() {} // standard says no
nontrivial nt;
};
Но это не сработает. Профсоюзы Constexpr должны иметь хотя бы одного активного члена. Это не может быть пустой союз. Чтобы обойти это ограничение, добавляется фиктивный член. Это делает std::optional
пригодным для использования в контексте constexpr.
(Спасибо @Barry!) Из [dcl.constexpr]/4 (выделено мной):
Определение конструктора constexpr, тело функции которого не равно = delete, должно дополнительно удовлетворять следующим требованиям:
- если класс является объединением, имеющим альтернативные члены ([class.union]), то точно один из них должен быть инициализирован;
если класс является объединяющим классом, но не является объединением, для каждого из его анонимных членов объединения, имеющих вариантные члены, должен быть инициализирован ровно один из них;
для не делегирующего конструктора каждый конструктор, выбранный для инициализации нестатических членов данных и подобъектов базового класса, должен быть конструктором constexpr;
- для делегирующего конструктора целевой конструктор должен быть конструктором constexpr.