Os enums C ++ são assinados ou não? E, por extensão, é seguro validar uma entrada verificando se ela é <= seu valor máximo e deixar de fora> = seu valor mínimo (assumindo que você começou em 0 e foi incrementado em 1)?
Os enums C ++ são assinados ou não? E, por extensão, é seguro validar uma entrada verificando se ela é <= seu valor máximo e deixar de fora> = seu valor mínimo (assumindo que você começou em 0 e foi incrementado em 1)?
Respostas:
Você não deve confiar em nenhuma representação específica. Leia o seguinte link . Além disso, o padrão diz que é definido pela implementação qual tipo integral é usado como o tipo subjacente para um enum, exceto que não deve ser maior do que int, a menos que algum valor não possa caber em int ou em um int sem sinal.
Resumindo: você não pode confiar que um enum seja assinado ou não.
Vamos à fonte. Aqui está o que o documento padrão C ++ 03 (ISO / IEC 14882: 2003) diz em 7.2-5 (declarações de enumeração):
O tipo subjacente de uma enumeração é um tipo integral que pode representar todos os valores do enumerador definidos na enumeração. É definido pela implementação qual tipo integral é usado como o tipo subjacente para uma enumeração, exceto que o tipo subjacente não deve ser maior que int, a menos que o valor de um enumerador não possa caber em um int ou int não assinado.
Resumindo, seu compilador pode escolher (obviamente, se você tiver números negativos para alguns de seus valores de enumeração, ele será assinado).
Você não deve depender deles serem assinados ou não. Se quiser torná-los explicitamente assinados ou não, você pode usar o seguinte:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
Você não deve confiar que ele seja assinado ou não. De acordo com o padrão, é definido pela implementação qual tipo integral é usado como o tipo subjacente para um enum. Na maioria das implementações, porém, é um número inteiro assinado.
Em C ++ 0x, enumerações fortemente tipadas serão adicionadas, o que permitirá que você especifique o tipo de enum, como:
enum X : signed int { ... }; // signed enum
enum Y : unsigned int { ... }; // unsigned enum
Mesmo agora, porém, alguma validação simples pode ser alcançada usando o enum como uma variável ou tipo de parâmetro como este:
enum Fruit { Apple, Banana };
enum Fruit fruitVariable = Banana; // Okay, Banana is a member of the Fruit enum
fruitVariable = 1; // Error, 1 is not a member of enum Fruit
// even though it has the same value as banana.
O compilador pode decidir se os enums são assinados ou não.
Outro método de validação de enums é usar o próprio enum como um tipo de variável. Por exemplo:
enum Fruit
{
Apple = 0,
Banana,
Pineapple,
Orange,
Kumquat
};
enum Fruit fruitVariable = Banana; // Okay, Banana is a member of the Fruit enum
fruitVariable = 1; // Error, 1 is not a member of enum Fruit even though it has the same value as banana.
Mesmo algumas respostas antigas tiveram 44 votos positivos, eu tendo a discordar de todas elas. Em suma, não acho que devemos nos preocupar com o underlying type
da enum.
Em primeiro lugar, o tipo Enum do C ++ 03 é um tipo distinto, sem conceito de sinal. Desde o padrão C ++ 03dcl.enum
7.2 Enumeration declarations
5 Each enumeration defines a type that is different from all other types....
Portanto, quando falamos sobre o sinal de um tipo enum, digamos, ao comparar 2 operandos enum usando o <
operador, estamos na verdade falando sobre a conversão implícita do tipo enum em algum tipo integral. É o sinal desse tipo integral que importa . E ao converter enum em tipo integral, esta declaração se aplica:
9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).
E, aparentemente, o tipo subjacente do enum não tem nada a ver com a Promoção Integral. Já que o padrão define Promoção Integral assim:
4.5 Integral promotions conv.prom
.. An rvalue of an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of the enumeration
(i.e. the values in the range bmin to bmax as described in 7.2: int, unsigned int, long, or unsigned long.
Portanto, se um tipo de enum se torna signed int
ou unsigned int
depende de se signed int
pode conter todos os valores dos enumeradores definidos, não o tipo subjacente do enum.
Veja minha pergunta relacionada Sinal do tipo Enum C ++ incorreto após a conversão para o tipo integral
-Wsign-conversion
. Nós o usamos para ajudar a detectar erros não intencionais em nosso código. Mas +1 por citar o padrão e apontar que um enum não tem nenhum tipo ( signed
versus unsigned
) associado a ele.
No futuro, com C ++ 0x, enumerações fortemente tipadas estarão disponíveis e terão várias vantagens (como segurança de tipo, tipos subjacentes explícitos ou escopo explícito). Com isso você poderá ter mais certeza do sinal do tipo.
Além do que outros já disseram sobre assinado / não assinado, aqui está o que o padrão diz sobre o intervalo de um tipo enumerado:
7.2 (6): "Para uma enumeração onde e (min) é o menor enumerador e e (max) é o maior, os valores da enumeração são os valores do tipo subjacente no intervalo b (min) a b (max ), onde b (min) eb (max) são, respectivamente, os menores e maiores valores do menor campo de bits que pode armazenar e (min) ee (max). É possível definir uma enumeração que possui valores não definidos por qualquer um de seus enumeradores. "
Então, por exemplo:
enum { A = 1, B = 4};
define um tipo enumerado onde e (min) é 1 e e (max) é 4. Se o tipo subjacente é int assinado, então o menor campo de bits necessário tem 4 bits, e se os ints em sua implementação são complemento de dois, então o intervalo válido de o enum é de -8 a 7. Se o tipo subjacente não tem sinal, então ele tem 3 bits e o intervalo é de 0 a 7. Verifique a documentação do compilador se você se importar (por exemplo, se você deseja converter valores integrais diferentes de enumeradores para o tipo enumerado, então você precisa saber se o valor está no intervalo da enumeração ou não - se não, o valor de enum resultante não é especificado).
Se esses valores são uma entrada válida para sua função pode ser uma questão diferente de serem valores válidos do tipo enumerado. Seu código de verificação provavelmente está preocupado com o primeiro e não com o último e, portanto, neste exemplo, deve pelo menos verificar> = A e <= B.
Marque com std::is_signed<std::underlying_type
+ enums com escopo padrão paraint
https://en.cppreference.com/w/cpp/language/enum implica:
main.cpp
#include <cassert>
#include <iostream>
#include <type_traits>
enum Unscoped {};
enum class ScopedDefault {};
enum class ScopedExplicit : long {};
int main() {
// Implementation defined, let's find out.
std::cout << std::is_signed<std::underlying_type<Unscoped>>() << std::endl;
// Guaranteed. Scoped defaults to int.
assert((std::is_same<std::underlying_type<ScopedDefault>::type, int>()));
// Guaranteed. We set it ourselves.
assert((std::is_same<std::underlying_type<ScopedExplicit>::type, long>()));
}
Compile e execute:
g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main
Resultado:
0
Testado no Ubuntu 16.04, GCC 6.4.0.