POD-структуры, содержащие постоянный элемент


С этим кодом:

struct A
{
    int i;
    const int b;
};

// The union is to verify that A is a type that can be used in a union.
union U
{
    A a;
    int b;
};

int main()
{
    U a = {1, 1};
    U b = {2, 1};
}

G++ версии 4.8.3 жалуется на ошибку:

a.cpp:9:4: error: member ‘A U::a’ with copy assignment operator not allowed in union
  A a;
    ^
a.cpp:9:4: note: unrestricted unions only available with -std=c++11 or -std=gnu++11

Но clang 3.5.0 компилирует этот код без ошибок. Какой из них правильный? Это ошибка компилятора?

Моя попытка решить эту проблему:

Из пункта 1 раздела 9.5 стандарта C++03:

В объединении не более одного элемента данных может быть активным в любое время, то есть значение не более одного элемента данных может храниться в объединении в любое время. [Примечание: для одного специального гарантий для того, чтобы упростить использование союзов: если стручок-Союз содержит несколько под-структуры, которые имеют общую исходную последовательность (9.2), и если объект этот под-тип соединение содержит один из под-структуры, разрешается осмотреть общую начальную последовательность из любого города Пец-структура членов; см. 9.2. ] Размер объединения достаточен, чтобы содержать самый большой из его членов данных. Каждый элемент данных выделяется так, как если бы он был единственным членом структуры. Профсоюз может имеют функции-члены (включая конструкторы и деструкторы), но не виртуальные (10.3) функции. Союз не должен иметь базовых классов. Объединение не должно использоваться в качестве базового класса. Объект класса с нетривиальным конструктором (12.1), нетривиальным конструктором копирования (12.8), нетривиальным деструктором (12.4) или нетривиальным оператором присваивания копий (13.5.3, 12.8) не может быть членом объединения, как и массив таких объектов. Если объединение содержит статический элемент данных или элемент программа справочного типа плохо сформирована.

Из стандартного раздела 12.8 стандарта C++03 пункты 10 и 11:

Если определение класса явно не объявляет оператор присваивания копии, он объявляется неявно. Неявно объявленный оператор присваивания копии для класса X будет иметь вид X& X::operator=(const X&), если каждый прямой базовый класс B Из X имеет оператор присваивания копии, параметр которого имеет тип const B&, const volatile B& или B, и для всех нестатических данных члены X, которые относятся к классу типа M (или его массиву), каждый такой тип класса имеет оператор присваивания копии, параметр которого имеет тип const M&, const volatile M& или M.

Otherwise, the implicitly declared copy assignment operator will have the form X& X::operator=(X&) ...

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

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

Редактировать: Команды компиляции:

clang++ a.cpp -o a
g++ a.cpp -o a

Edit2: Чтобы показать, что g++ не жалуется на то, что A::b является const, но у A нет конструктора, я также попробовал эту программу:

struct A
{
    int i;
    const int b;
};

int main()
{
    A a = {1, 1};
}

Это скомпилировано без ошибок как на g++, так и на clang++:

g++ b.cpp -o b
clang++ b.cpp -o b
1 11

1 ответ:

Как вы правильно заметили, оператор присваивания копии неявнообъявлен и тривиален. То же самое относится и к конструктору по умолчанию, который также тривиален и неявно объявлен.

Обратите внимание, что обе эти функции-члены не определены неявно - это происходит только тогда, когда они используются, [класс.ctor] / 7:

Неявно объявленный конструктор по умолчанию для класса-это неявно определено , когда оно используется для создания объекта его тип класса (1.8).

.. что здесь явно не так.
Это ключевое различие, и причина, по которой цитата @dasblinkenlight не имеет отношения к этому вопросу: конструктор по умолчанию никогда не определяется, поэтому абзац об отсутствующем mem-initializer-ids не применяется.

Как тогда связаны члены const и оператор присваивания? Здесь:

Неявно объявленный оператор присваивания копий является неявно определенным Когда объекту его типа класса присваивается значение его типа класса или значение типа класса, производное от его типа класса. Программа плохо сформирована, если класс, для которого выполняется копирование присваивания оператор неявно определен имеет:

  • нестатический элемент данных типаconst , или [..]
Таким образом, программа будет плохо сформирована, если будет использоваться оператор присваивания копий. Но это не так. Все специальные функции-члены только объявленные, и любые ограничения на const - ность нестатических членов данных применяются только к неявно определенным специальным функциям-членам.

В качестве примера возьмем

struct A
{
    int i;
    const int b;
};

int main()
{
    A a = {1, 1};
}

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