C++ Конструкторы по умолчанию в объединении с вариантом члена с нетривиальным конструктором по умолчанию

Я недавно прочитал описание конструкторов по умолчанию для объединений: Конструктор по умолчанию

Существует следующее правило:

Blockquote Удален неявно объявленный конструктор по умолчанию: [...] T является объединением, по крайней мере, с одним вариантом члена с нетривиальным конструктором по умолчанию, и ни у одного варианта члена T нет инициализатора члена по умолчанию.[...]

Тогда я решил сделать упражнение и проверить правило.

struct Member {
 public:
  // Member() : mX(0) {}
  virtual int GetX() {
    return mX;
  }
  int mX;
};

union DefaultConstructor {
  int mA;
  Member mMember;
  int GetA();
};

С использованием gcc v5.3.1 (я знаю, что он довольно старый) я получаю ожидаемую ошибку:

> ../src/DefaultConstrcutors.cpp: In function ‘void
> Test_DefaultConstructors()’: ../src/DefaultConstrcutors.cpp:26:22:
> error: use of deleted function
> ‘DefaultConstructor::DefaultConstructor()’    DefaultConstructor foo;
>                       ^ In file included from ../src/DefaultConstrcutors.cpp:19:0:
> ../src/DefaultConstructors.h:155:7: note:
> ‘DefaultConstructor::DefaultConstructor()’ is implicitly deleted
> because the default definition would be ill-formed:  union
> DefaultConstructor {
>        ^ ../src/DefaultConstructors.h:157:10: error: union member ‘DefaultConstructor::mMember’ with non-trivial ‘Member::Member()’   
> Member mMember;
>           ^

Итак, согласно правилу, если я предоставлю инициализатор элемента по умолчанию для варианта элемента, то он должен скомпилироваться. Поэтому я изменил определение объединения на:

union DefaultConstructor {
      int mA = 0;
      Member mMember;
      int GetA();
    };

И теперь он должен скомпилироваться, но я получил ту же ошибку.

Следующим шагом было предоставление инициализатора по умолчанию для mMember вместо mA (только один вариантный вариант объединения может иметь инициализатор по умолчанию).

union DefaultConstructor {
      int mA;
      Member mMember{};
      int GetA();
    };

Теперь это компилируется.

Вопрос: почему он не скомпилировался во втором случае, когда у mA был инициализатор по умолчанию? Согласно упомянутому правилу это должно быть возможно. Какое более похожее правило предусмотрено здесь: Декларация Союза

Если объединение содержит нестатический элемент данных с нетривиальным конструктором по умолчанию, конструктор объединения по умолчанию удаляется по умолчанию, если только вариантный член объединения не имеет инициализатора элемента по умолчанию.

У кого-нибудь есть идея, почему она не работает?

Привет, Петр

1 ответ

Решение

Cppreference, как правило, хороший источник информации, но здесь компилятор прав. Проект n3337 для C++11 содержит в 9.5 Unions [class.union] ненормативную, но явную заметку, в которой говорится:

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

Нет упоминания о том, что это не будет иметь место, если один элемент имеет инициализатор элемента по умолчанию.

Та же самая заметка все еще стоит в проекте n4296 для C++14, поэтому я предполагаю, что она должна быть такой же в фактическом стандарте C++. Примечания, конечно, не являются нормативными, но их цель состоит в том, чтобы сделать более четкой интерпретацию стандарта, поэтому я предполагаю, что интерпретация cppreference является неправильной, поскольку она не относится к этой заметке и что интерпретация gcc является правильной.

Другие вопросы по тегам