Pregunta

¿Las enumeraciones de C ++ están firmadas o sin firmar? Y, por extensión, ¿es seguro validar una entrada comprobando que es & Lt; = su valor máximo y omita & Gt; = su valor mínimo (suponiendo que comenzó en 0 e incrementó en 1)?

¿Fue útil?

Solución

No debe confiar en ninguna representación específica. Lea el siguiente enlace . Además, el estándar dice que está definido por la implementación qué tipo integral se usa como el tipo subyacente para una enumeración, excepto que no debe ser mayor que int, a menos que algún valor no pueda caber en int o un int sin signo.

En resumen: no puede confiar en que una enumeración esté firmada o sin firmar.

Otros consejos

Vayamos a la fuente. Esto es lo que dice el documento estándar C ++ 03 (ISO / IEC 14882: 2003) en 7.2-5 (Declaraciones de enumeración):

  

El tipo subyacente de una enumeración   es un tipo integral que puede representar   todos los valores de enumerador definidos en   La enumeración. Es   implementación definida que integral   tipo se utiliza como el tipo subyacente   para una enumeración, excepto que el   el tipo subyacente no debe ser más grande   que int a menos que el valor de un   el enumerador no puede caber en un int o   unsigned int.

En resumen, su compilador puede elegir (obviamente, si tiene números negativos para algunos de sus valores de enumeración, se firmará).

No debe depender de que estén firmados o no firmados. Si desea hacerlos explícitamente firmados o sin firmar, puede usar lo siguiente:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

No debe confiar en que esté firmado o sin firmar. De acuerdo con el estándar, se define la implementación del tipo integral que se utiliza como tipo subyacente para una enumeración. Sin embargo, en la mayoría de las implementaciones, es un entero con signo.

En C ++ 0x se agregarán enumeraciones fuertemente tipadas que le permitirá especificar el tipo de una enumeración como:

enum X : signed int { ... };    // signed enum
enum Y : unsigned int { ... };  // unsigned enum

Incluso ahora, sin embargo, se puede lograr una validación simple usando la enumeración como una variable o 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.

El compilador puede decidir si las enumeraciones están o no firmadas o no.

Otro método para validar las enumeraciones es utilizar la enumeración misma como un tipo de variable. Por ejemplo:

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.

Incluso algunas respuestas antiguas obtuvieron 44 votos a favor, tiendo a estar en desacuerdo con todos ellos. En resumen, no creo que debamos preocuparnos por la underlying type de la enumeración.

Primero, el tipo C ++ 03 Enum es un tipo distinto que no tiene concepto de signo. Desde C ++ 03 estándar dcl.enum

7.2 Enumeration declarations 
5 Each enumeration defines a type that is different from all other types....

Entonces, cuando estamos hablando del signo de un tipo enum, digamos que al comparar 2 operandos enum usando el operador <, en realidad estamos hablando de convertir implícitamente el tipo enum a algún tipo integral. Es el signo de este tipo integral lo que importa . Y al convertir enum a tipo integral, se aplica esta declaración:

9 The value of an enumerator or an object of an enumeration type is converted to an integer by integral promotion (4.5).

Y, aparentemente, el tipo subyacente de la enumeración no tiene nada que ver con la Promoción Integral. Como el estándar define la promoción integral de esta manera:

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.

Entonces, si un tipo de enumeración se convierte en signed int o unsigned int depende de si <=> puede contener todos los valores de los enumeradores definidos, no el tipo subyacente de la enumeración.

Ver mi pregunta relacionada Signo del tipo de enumeración de C ++ incorrecto después de convertir a integral Escriba

En el futuro, con C ++ 0x, enumeraciones fuertemente tipadas estará disponible y tendrá varias ventajas (como seguridad de tipo, tipos subyacentes explícitos o alcance explícito). Con eso, podría estar más seguro del signo del tipo.

Además de lo que otros ya han dicho sobre firmado / no firmado, esto es lo que dice el estándar sobre el rango de un tipo enumerado:

7.2 (6): " Para una enumeración donde e (min) es el enumerador más pequeño y e (max) es el más grande, los valores de la enumeración son los valores del tipo subyacente en el rango b (min) a b (max), donde b (min) yb (max) son, respectivamente, los valores más pequeños y más grandes del campo de bits más pequeño que puede almacenar e (min) y e (max). Es posible definir una enumeración que tenga valores no definidos por ninguno de sus enumeradores. & Quot;

Entonces, por ejemplo:

enum { A = 1, B = 4};

define un tipo enumerado donde e (min) es 1 y e (max) es 4. Si el tipo subyacente está firmado int, entonces el campo de bits requerido más pequeño tiene 4 bits, y si los ints en su implementación son dos, entonces el el rango válido de la enumeración es de -8 a 7. Si el tipo subyacente no está firmado, entonces tiene 3 bits y el rango es de 0 a 7. Consulte la documentación del compilador si le importa (por ejemplo, si desea emitir valores integrales distintos de enumeradores al tipo enumerado, entonces necesita saber si el valor está en el rango de la enumeración o no, si no, el valor de enumeración resultante no está especificado).

Si esos valores son entradas válidas para su función puede ser un problema diferente de si son valores válidos del tipo enumerado. Su código de verificación probablemente esté preocupado por el primero en lugar de por el segundo, por lo que en este ejemplo al menos debería estar revisando & Gt; = A y & Lt; = B.

Verifíquelo con std::is_signed<std::underlying_type + enumeraciones de ámbito predeterminadas a int

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>()));
}

GitHub upstream .

Compilar y ejecutar:

g++ -std=c++17 -Wall -Wextra -pedantic-errors -o main main.cpp
./main

Salida:

0

Probado en Ubuntu 16.04, GCC 6.4.0.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top