Почему профсоюзы имеют удаленный конструктор по умолчанию, если только один из его членов не имеет одного?

N3797::9.5/2 [class.union] говорит:

Если какой-либо нестатический член данных объединения имеет нетривиальное значение по умолчанию конструктор (12.1), конструктор копирования (12.8), конструктор перемещения (12.8), оператор присваивания копии (12.8), оператор присваивания перемещения (12.8) или деструктор (12.4), соответствующая членная функция объединения должна быть предоставленным пользователем или он будет неявно удален (8.4.3) для объединение

Я пытался понять эту заметку на примере:

#include <iostream>
#include <limits>

struct A
{
    A(const A&){ std::cout << "~A()" << std::endl; } //A has no default constructor
};

union U
{
    A a;
};

U u; //error: call to implicitly-deleted default constructor of 'U'

int main()
{

}

DEMO

Это поведение мне не совсем понятно. struct A не имеет неявно объявленного конструктора по умолчанию, потому что 12.1/4: [class.ctor] говорит:

Если для класса X нет объявленного пользователем конструктора, конструктор без параметров неявно объявляется как дефолт (8.4).

Это означает, что struct A не имеет нетривиального конструктора по умолчанию (вообще нет конструктора по умолчанию, в частности нетривиального). Для этого union U не нужно иметь удаленный конструктор по умолчанию. Что не так?

Ответ 1

Соответствующая формулировка находится в С++ 11 [class.ctor] p5 (выделение мое):

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

[...]

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

[...]

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

[...]

В вашем классе A нет конструктора по умолчанию, поэтому стандартный конструктор по умолчанию (неявный или явный) для класса X (будь то объединенный или неединичный), содержащий нестатический член данных типа A без инициализатора приводит к удалению конструктора по умолчанию для X. Он должен: просто компилятор не может сгенерировать любой другой конструктор по умолчанию.

Что касается вашего последующего вопроса в комментариях:

Если вместо A не иметь конструктора по умолчанию, он имеет нетривиальный конструктор по умолчанию, то есть разница между использованием этого в объединении и в неединичном классе, а также частью [ class.ctor] p5: это первая пуля, в которую я включил, без особого внимания, в мою предыдущую цитату.

Ответ 2

В вашем примере проблема заключается не в том, что у вашего кода нет нетривиального конструктора defautl, но у него есть конструктор копирования.

Но, как правило, a union имеет несколько членов: "не более одного из нестатических членов данных может быть активным в любое время, то есть значение не более одного из нестатических элементов данных может быть хранится в союзе в любое время" (9.5/1).

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

union W {
    A a; 
    int i; 
};

Когда вы создадите объект:

W w;  

как этот объект будет создан по умолчанию? Какой член должен быть активным? Как следует копировать этот объект по умолчанию? Должно быть построено/скопировано A или int?

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

union W
{
    int i;
    A a;
    W() { /*...*/ }
    W(const W&c) { /*...*/ }
};

В этом документе подробно объясняется обоснование и формулировка С++ 11 по этой теме.

Важное замечание: К сожалению, неограниченные объединения не поддерживаются MSVC13: он все еще не принимает ЛЮБОЙ член, имеющий ЛЮБОЙ из заданного пользователем функции нетривиального значения. GCC принимает его начиная с 4.6 и clang начиная с версии 3.1.