Почему ограниченные перечисления позволяют использовать | оператор при инициализации с использованием ранее назначенных значений?

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

Stroustroup, C++ Язык программирования, 4-е издание, Раздел 8.4.1, документирует, что enum-классы с областью видимости неявно преобразуются в целочисленные типы, и предоставляет код для операторов | и & в качестве примера того, как использовать static_cast чтобы обойти это.

Не должна ли следующая инициализация с использованием оператора | для ранее определенных значений enum быть недопустимой?

enum class FileCopy {
    PreviousHDUs   = 1,
    CurrentHDU     = 2,
    FollowingHDUs  = 4,
    AllHDUs        = PreviousHDUs | CurrentHDU | FollowingHDUs,
    CurrentHeader  = 8
};

int main()
{
    std::cout << static_cast<int>( FileCopy::AllHDUs) << "\n";
}

Я проверил это на Wandbox, используя clang и gcc HEAD с --pedantic-errors, и он компилирует и возвращает ожидаемый результат, 7. Это не сказать, что это законно, просто это, кажется, принято компиляторами.

Это явно задокументированное поведение? Мне не удалось проанализировать документацию таким образом, чтобы описать это поведение.

Ответы

Ответ 1

[Dcl.enum]/5:

... Если базовый тип является фиксированным, тип каждого перечислителя перед закрывающей скобкой является базовым типом...

То есть каждый перечислитель имеет тип int, пока не встретится закрывающая скобка. После этого перечислители имеют тип FileCopy, и вы больше не сможете поразрядно-ИЛИ их вместе, как это.

Ответ 2

В соответствии со стандартом С++ 17 (8.5.13 оператор ИБЛИ с побитовым включением)

1 Выполняются обычные арифметические преобразования (8.3); результат побитовая включающая функция ИЛИ своих операндов. Оператор применяется только к целочисленным или незаданным операндам перечисления.

И (10.2 Объявления перечисления)

  1. ... Для перечисляемого типа с ограниченным диапазоном базовым типом является int, если это не так явно указано. В обоих этих случаях тип считается фиксированным. После закрывающей скобки спецификатор enum, каждый перечислитель имеет тип своего перечисления. Если базовый тип фиксирован, тип каждого перечислителя до закрывающая скобка является базовым типом и константным выражением в определение перечислителя должно быть преобразованным константным выражением базовый тип

Так что это явно задокументированное поведение.