Базовый тип определяется реализацией.Он должен иметь возможность представлять каждый перечислитель и не может быть больше int
, если не требуется.Не существует требования в отношении подписи (за исключением того, что базовый тип должен иметь возможность представлять каждый перечислитель), для dcl.enum.7 , как вы уже нашли.Это ограничивает обратное распространение типов перечислителей больше, чем вы предполагаете.Примечательно, что нигде не говорится, что базовый тип перечисления должен быть типом любого из инициализаторов перечислителей.
Clang предпочитает целые числа без знака как основы перечисления над целыми числами со знаком;это все, что нужно сделать.Важно отметить, что тип перечисления не должен соответствовать какому-либо конкретному типу перечислителя: он должен только представлять каждого перечислителя.Это вполне нормально и понятно в других контекстах.Например, если у вас есть EN_1 = 1
, вас не удивит, что базовый тип перечисления не int
или unsigned int
, даже если 1 является int
.
Вы такжеправильно сказать, что тип 0x7fffffffffffffff
равен long
.Clang согласен с вами, однако он неявно преобразует константу в unsigned long
:
TranslationUnitDecl
`-EnumDecl <line:1:1, line:5:1> line:1:6 Foo
|-EnumConstantDecl <line:2:5> col:5 Frob 'Foo'
|-EnumConstantDecl <line:3:5> col:5 Bar 'Foo'
`-EnumConstantDecl <line:4:5, col:11> col:5 Baz 'Foo'
`-ImplicitCastExpr <col:11> 'unsigned long' <IntegralCast>
`-IntegerLiteral <col:11> 'long' 576460752303423487
Это разрешено, поскольку, как мы уже говорили, базовый тип перечисления не нуждается вбыть дословным типом любого перечислителя.
Когда стандарт говорит, что каждый перечислитель имеет тип перечисления, это означает, что тип EN_1
равен enum UEn
после закрывающей скобки перечисления.Обратите внимание, что «после закрывающей скобки» и «до закрывающей скобки» упоминаются.
Перед закрывающей скобкой, если перечисление не имеет фиксированного типа, тогда тип каждого перечислителя - это тип его инициализирующего выражениятипа, но это только временно.Это то, что позволяет, например, писать EN_2 = EN_1 + 1
без приведения EN_1
даже в области enum class
.Это больше не верно после закрывающей скобки.Вы можете заставить компилятор показывать вам, просматривая сообщения об ошибках или просматривая разборку:
template<typename T>
T tell_me(const T&& value);
enum Foo {
Baz = 0x7ffffffffffffff,
Frob = tell_me(Baz)
// non-constexpr function 'tell_me<long>' cannot be used in a constant expression
};
Обратите внимание, что в этом случае T
было определено как long
, но после закрывающей скобки этоПредполагается, что Foo
:
template<typename T>
T tell_me(const T&& value);
enum Foo {
Baz = 0x7ffffffffffffff
};
int main() {
tell_me(Baz);
// call Foo tell_me<Foo>(Foo const&&)
}
Если вы хотите, чтобы ваш тип перечисления был подписан с помощью Clang, вам нужно указать его с использованием синтаксиса : base_type
, или вам нужен отрицательный перечислитель.