Pregunta ¿Está permitido que una enumeración tenga un valor no listado? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Decir, tenemos

enum E
{
  Foo = 0,
  Bar = 1
};

Ahora, lo hacemos

enum E v = ( enum E ) 2;

Y entonces

switch ( v )
{
  case Foo:
    doFoo();
  break;
  case Bar:
    doBar();
  break;
  default:
    // Is the compiler required to honor this?
    doOther();
  break;
}

Como el interruptor anterior maneja todos los valores posibles enumerados de la enumeración, ¿está permitido que el compilador optimice el default sucursal arriba, o de otra manera tener un comportamiento no especificado o indefinido en el caso de que el valor de enum no esté en la lista?

Como estoy esperando que el comportamiento sea similar para C y C ++, la pregunta es sobre ambos idiomas. Sin embargo, si hay una diferencia entre C y C ++ para ese caso, sería bueno saberlo también.


32
2017-11-19 19:44


origen


Respuestas:


Situación C ++

En C ++, cada enumeración tiene un tipo integral subyacente. Puede ser arreglado, si está explícitamente especificado (ej: enum test2 : long { a,b};) o int por defecto en el caso de un con alcance enum (por ejemplo: enum class test { a,b };)

7.2 / 5: Cada enumeración define un tipo que es diferente de todos los demás tipos. Cada enumeración también tiene un tipo subyacente. (...) si no   explícitamente especificado, el tipo subyacente de un tipo de enumeración con ámbito   es int. En estos casos, se dice que el tipo subyacente es fijo.

En el caso de un sin cobertura enum donde el tipo subyacente no fue explícitamente fijado (tu ejemplo), el estándar le da más flexibilidad a su compilador:

7.2 / 6: Para una enumeración cuyo tipo subyacente no es fijo, el tipo subyacente es un tipo integral que puede representar todo el   valores del enumerador definidos en la enumeración. (...) Es la implementación definida qué tipo integral se utiliza como subyacente   tipo excepto que el tipo subyacente no debe ser mayor que int   a menos que el valor de un enumerador no pueda caber en un int o unsigned   En t.

Ahora un algo muy complicado: los valores que puede contener una variable enum dependen de si el tipo subyacente es fijo o no:

  • si es fijo, "los valores de la enumeración son los valores de tipo subyacente ". 

  • otherwhise, son los valores integrales dentro del mínimo y el máximo del bit-field más pequeño que pueden contener el enumerador más pequeño y el más grande.

Estás en el segundo caso, aunque tu código funcionará en la mayoría de los compiladores, el bitfield más pequeño tiene un tamaño de 1 y por lo tanto los únicos valores que puedes mantener con seguridad en todos los compiladores C ++ compatibles son aquellos entre 0 y 1 ... Si desea asegurarse de que puede establecer el valor de 2, debe hacerlo enum con ámbito o indicar explícitamente un tipo subyacente. 

Más lectura:
 

Situación C

La situación C es mucho más simple:

6.2.5 / 16: Una enumeración comprende un conjunto de valores constantes enteros nombrados. Cada enumeración distinta constituye una enumeración diferente   tipo.

Entonces, básicamente, es un int:

6.7.2.2./2 La expresión que define el valor de una constante de enumeración debe ser una expresión constante entera que tiene un valor   representable como un int.

Con la siguiente restricción:

Cada tipo enumerado debe ser compatible con caracteres, un entero con signo   tipo, o un tipo entero sin signo. La elección del tipo es   definido por la implementación, 128) pero debe ser capaz de representar el   valores de todos los miembros de la enumeración.


18
2017-11-19 21:39



En C an enum tipo es un tipo entero lo suficientemente grande como para contener todo el enum constantes:

(C11, 6.7.2.2p4) "Cada tipo enumerado debe ser compatible con char, un tipo entero con signo o un tipo entero sin signo. La elección del tipo está definida por la implementación, 110) pero debe ser capaz de representar los valores de todos los miembros de la enumeración ".

Digamos el tipo seleccionado para enum E es _Bool. UN _Bool objeto solo puede almacenar los valores 0 y 1. No es posible tener un _Bool objeto que almacena un valor diferente de 0 o 1 sin invocar un comportamiento indefinido

En ese caso, el compilador puede suponer que un objeto del enum E tipo solo puede contener 0 o 1 en un programa estrictamente conforme y se le permite optimizar el default caja del interruptor.


3
2017-11-19 20:22



C ++ Std 7.2.7 [dcl.enum]:

Es posible definir una enumeración que tenga valores no definidos por ninguno de sus enumeradores.

Por lo tanto, puede tener valores de enumeración que no figuran en la lista de enumeradores.

Pero en su caso específico, el 'tipo subyacente' no es 'fijo' (7.2.5). La especificación no especifica cuál es el tipo subyacente en ese caso, pero debe ser integral. Como char es el tipo más pequeño, podemos concluir que hay otros valores de la enumeración que no están especificados en la lista de enumeradores.

Por cierto, creo que el compilador puede optimizar su caso cuando puede determinar que no hay otros valores asignados a v, lo cual es seguro, pero creo que no hay compiladores que sean tan inteligentes todavía.


0
2017-11-19 20:08



Además, 7.2 / 10:

Una expresión de tipo aritmético o de enumeración se puede convertir a   enumeración tipo explícitamente. El valor no cambia si está en el   rango de valores de enumeración del tipo de enumeración; de lo contrario,   el valor de enumeración resultante no está especificado.


0
2017-11-19 20:16



En C los enumeradores tienen tipo int  . Por lo tanto, cualquier valor entero se puede asignar a un objeto del tipo de enumeración.

Del estándar C (6.7.2.2 especificadores de enumeración)

3 Los identificadores en una lista de enumeradores se declaran como constantes que   tener tipo En t y puede aparecer donde sea que estén permitidos.

En C ++, los enumeradores tienen el tipo de enumeración que lo define. En C ++, debe especificar explícitamente el tipo subyacente o el compilador se calcula el valor máximo permitido.

Del Estándar C ++ (7.2 declaraciones de enumeración)

5 Cada enumeración define un tipo que es diferente de todos los demás tipos. Cada enumeración también tiene un tipo subyacente. El tipo subyacente se puede especificar explícitamente usando enum-base; si no se especifica explícitamente, el tipo subyacente de un tipo de enumeración con ámbito es int. En estos casos, se dice que el tipo subyacente es fijo. Siguiendo el corchete de cierre de un enum-specifier, cada enumerador tiene el tipo de su enumeración.

Por lo tanto, en C, cualquier valor posible de una enumeración es cualquier valor entero. El compilador puede no optimizar un interruptor que elimina la etiqueta predeterminada.


-1
2017-11-19 19:55



En C y C ++, esto puede funcionar.

Mismo código para ambos:

#include <stdio.h>

enum E
{
  Foo = 0,
  Bar = 1
};

int main()
{
    enum E v = (enum E)2;    // the cast is required for C++, but not for C
    printf("v = %d\n", v);
    switch (v) {
    case Foo:
        printf("got foo\n");
        break;
    case Bar:
        printf("got bar\n");
        break;
    default:
        printf("got \n", v);
        break;
    }
}

Mismo resultado para ambos:

v = 2
got default

En C, una enum es un tipo integral, por lo que puede asignarle un valor entero sin conversión. En C ++, un enum es su propio tipo.


-1
2017-11-19 19:56