Перечисления, превышающие размер типа наибольшее число

Я хочу полностью понять, как компилятор C++ работает с enum, превышающим максимально возможное число, то есть содержащим -1 и UINT64_MAX одновременно, т.е.

enum A {
    X = -1,
    Y = UINT64_MAX
};

Сначала я подумал, что компилятор не примет этот код. На самом деле он не компилируется, когда enum заменяется enum class, но приведенный выше пример компилируется. Согласно стандарту мы имеем для базового типа:

Объявляет тип перечисления с незаданной областью, базовый тип которого не является фиксированным (в этом случае базовый тип является целочисленным типом, определяемым реализацией, который может представлять все значения перечислителя; этот тип не больше int, если значение перечислителя не может поместиться в int или unsigned int. Если список-перечислитель пуст, базовый тип такой, как если бы перечисление имело единственный перечислитель со значением 0). (https://en.cppreference.com/w/cpp/language/enum)

Но что это значит для моего примера?

Я написал небольшую примерную программу, чтобы узнать, что происходит:

#include <iostream>
#include <cstdint>

enum A {
    X = -1,
    XX = -1,
    Y = UINT64_MAX
};

int main()
{

    std::cout << "X unsigned: " << (uint64_t)(X) << ", signed: " << (int64_t)(X) << std::endl;
    std::cout << "Y unsigned: " << (uint64_t)(Y) << ", signed: " << (int64_t)(Y) << std::endl;

    std::cout << "(X == XX) == " << (X == XX) << std::endl;
    std::cout << "(X == Y) == " << (X == Y) << std::endl;
}

Выход:

X unsigned: 18446744073709551615, signed: -1
Y unsigned: 18446744073709551615, signed: -1
(X == XX) == 1
(X == Y) == 0

Теперь я в замешательстве. Очевидно, X и Y представляют одно и то же число, но они все еще различимы, т.е. Сравнение X == Y неверно (но X=XX на самом деле верно). Что здесь происходит?

Я знаю, что лучше использовать не старый enum, а новый enum class. Но все-таки enum широко используется, и я хочу понять, что здесь происходит.

Ответ 1

Ваш компилятор, скорее всего, использует 128-битный целочисленный тип со знаком в качестве базового типа в соответствии со стандартом C++.

Убедитесь сами

std::cout << sizeof(std::underlying_type<A>::type);

Ссылка: https://ideone.com/z4K0rz, выходы 16.

Вывод, который вы наблюдаете, согласуется с сужающим преобразованием этого в 64-битный тип без знака.

Ответ 2

То, что вы видите, является эффектом приведения типов, а не enum. Ваш вывод зависит от того, как вы разыгрываете значение.

Попробуйте это: он имеет тот же результат, что и ваш, без enum.

#include <iostream>
#include <cstdint>

int main()
{

    std::cout << "X unsigned: " << (uint64_t)(-1) << ", signed: " << (int64_t)(-1) << std::endl;
    return 0;
}

И вывод:

X unsigned: 18446744073709551615, signed: -1