Estoy tratando de hacer algo como lo siguiente:
enum E;
void Foo(E e);
enum E {A, B, C};
que el compilador rechaza. He echado un vistazo rápido a Google y el consenso parece ser "no puedes hacerlo", pero no puedo entender por qué. ¿Alguien puede explicar?
Aclaración 2: estoy haciendo esto ya que tengo métodos privados en una clase que toman dicha enumeración, y no quiero que se expongan los valores de la enumeración, por lo que, por ejemplo, no quiero que nadie sepa que E se define como
enum E {
FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}
ya que el proyecto X no es algo que quiero que mis usuarios sepan.
Entonces, quería reenviar declarar la enumeración para poder poner los métodos privados en el archivo de encabezado, declarar la enumeración internamente en el cpp y distribuir el archivo y el encabezado de la biblioteca construida a las personas.
En cuanto al compilador, es GCC.
Respuestas:
La razón por la que no se puede declarar la enumeración en adelante es que sin conocer los valores, el compilador no puede conocer el almacenamiento requerido para la variable enum. Los compiladores de C ++ pueden especificar el espacio de almacenamiento real en función del tamaño necesario para contener todos los valores especificados. Si todo lo que está visible es la declaración directa, la unidad de traducción no puede saber qué tamaño de almacenamiento se habrá elegido; podría ser un char o un int, o algo más.
De la Sección 7.2.5 del Estándar ISO C ++:
Dado que la persona que llama a la función debe conocer los tamaños de los parámetros para configurar correctamente la pila de llamadas, el número de enumeraciones en una lista de enumeración debe conocerse antes del prototipo de la función.
Actualización: en C ++ 0X se ha propuesto y aceptado una sintaxis para declarar tipos de enumeración anteriores. Puede ver la propuesta en http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
fuente
struct S; void foo(S s);
(tenga en cuenta quefoo
solo se declara, no se define), entonces no hay ninguna razón por la que no podamos hacerloenum E; void foo(E e);
también. En ambos casos, el tamaño no es necesario.La declaración directa de enumeraciones es posible desde C ++ 11. Anteriormente, la razón por la que los tipos de enumeración no se podían declarar hacia adelante es porque el tamaño de la enumeración depende de su contenido. Mientras la aplicación especifique el tamaño de la enumeración, se puede declarar hacia adelante:
fuente
Estoy agregando una respuesta actualizada aquí, dados los desarrollos recientes.
Puede declarar hacia adelante una enumeración en C ++ 11, siempre que declare su tipo de almacenamiento al mismo tiempo. La sintaxis se ve así:
De hecho, si la función nunca se refiere a los valores de la enumeración, no necesita la declaración completa en ese momento.
Esto es compatible con G ++ 4.6 y versiones posteriores (
-std=c++0x
o-std=c++11
en versiones más recientes). Visual C ++ 2013 lo admite; en versiones anteriores tiene algún tipo de soporte no estándar que aún no he descubierto: encontré alguna sugerencia de que una declaración directa simple es legal, pero YMMV.fuente
enum class
como una extensión de C ++ (antes de C ++ 11 diferenteenum class
), al menos si no recuerdo mal. El compilador le permitía especificar el tipo subyacente de una enumeración, pero noenum class
admitía enumeraciones declaradas hacia adelante, y le advirtió que calificar un enumerador con el alcance de la enumeración era una extensión no estándar. Recuerdo que funciona más o menos lo mismo que especificar el tipo subyacente en C ++ 11, excepto que es más molesto porque tuvo que suprimir la advertencia.Declarar cosas en C ++ es muy útil porque acelera dramáticamente el tiempo de compilación . Se puede declarar adelante varias cosas en C ++, incluyendo:
struct
,class
,function
, etc ...Pero, ¿puedes reenviar una declaración
enum
en C ++?No, no puedes.
¿Pero por qué no permitirlo? Si se permitiera, podría definir su
enum
tipo en su archivo de encabezado y susenum
valores en su archivo de origen. Parece que debería permitirse, ¿verdad?Incorrecto.
En C ++ no hay un tipo predeterminado,
enum
como en C # (int). En C ++ suenum
compilador determinará su tipo como cualquier tipo que se ajuste al rango de valores que tiene para suenum
.Qué significa eso?
Significa que el
enum
tipo subyacente de su no puede determinarse completamente hasta que tenga todos los valoresenum
definidos. En qué hombre no puede separar la declaración y la definición de suenum
. Y, por lo tanto, no puede reenviar una declaraciónenum
en C ++.El estándar ISO C ++ S7.2.5:
Puede determinar el tamaño de un tipo enumerado en C ++ utilizando el
sizeof
operador. El tamaño del tipo enumerado es el tamaño de su tipo subyacente. De esta manera, puede adivinar qué tipo está utilizando su compilador para suenum
.¿Qué pasa si especifica el tipo de su
enum
explícitamente así:¿Puedes entonces declarar tu
enum
?No. ¿Pero por qué no?
Especificar el tipo de un
enum
no es realmente parte del estándar actual de C ++. Es una extensión VC ++. Sin embargo, será parte de C ++ 0x.Fuente
fuente
[Mi respuesta es incorrecta, pero la he dejado aquí porque los comentarios son útiles].
La declaración directa de enumeraciones no es estándar, porque no se garantiza que los punteros a diferentes tipos de enumeración sean del mismo tamaño. Es posible que el compilador necesite ver la definición para saber qué punteros de tamaño se pueden usar con este tipo.
En la práctica, al menos en todos los compiladores populares, los punteros a las enumeraciones tienen un tamaño constante. La declaración directa de enumeraciones se proporciona como una extensión de lenguaje por Visual C ++, por ejemplo.
fuente
De hecho, no hay tal cosa como una declaración anticipada de enumeración. Como la definición de una enumeración no contiene ningún código que pueda depender de otro código que use la enumeración, generalmente no es un problema definir la enumeración por completo cuando la declara por primera vez.
Si el único uso de su enumeración es por funciones de miembros privados, puede implementar la encapsulación teniendo la enumeración como un miembro privado de esa clase. La enumeración aún debe definirse por completo en el punto de declaración, es decir, dentro de la definición de clase. Sin embargo, este no es un problema mayor ya que declarar funciones de miembros privados allí, y no es una exposición peor de los aspectos internos de implementación que eso.
Si necesita un mayor grado de ocultación de los detalles de su implementación, puede dividirlo en una interfaz abstracta, que solo consta de funciones virtuales puras y una clase concreta, completamente oculta, que implementa (hereda) la interfaz. La creación de instancias de clase puede ser manejada por una fábrica o una función miembro estática de la interfaz. De esa manera, incluso el nombre real de la clase, y mucho menos sus funciones privadas, no se expondrá.
fuente
Solo notando que la razón es que el tamaño de la enumeración aún no se conoce después de la declaración hacia adelante. Bueno, utiliza la declaración hacia adelante de una estructura para poder pasar un puntero o hacer referencia a un objeto desde un lugar al que también se hace referencia en la definición de estructura declarada hacia adelante.
Declarar adelante una enumeración no sería demasiado útil, porque uno desearía poder pasar la enumeración por valor. Ni siquiera podría tener un puntero, porque recientemente me dijeron que algunas plataformas usan punteros de diferente tamaño para char que para int o long. Entonces todo depende del contenido de la enumeración.
El estándar actual de C ++ no permite explícitamente hacer algo como
(en
7.1.5.3/1
). Pero el próximo estándar C ++ debido al próximo año permite lo siguiente, lo que me convenció de que el problema realmente tiene que ver con el tipo subyacente:Se conoce como una declaración de enumeración "opaca". Incluso puede usar X por valor en el siguiente código. Y sus enumeradores se pueden definir más adelante en una redeclaración posterior de la enumeración. Ver
7.2
en el borrador de trabajo actual.fuente
Lo haría de esta manera:
[en el encabezado público]
[en el encabezado interno]
Al agregar FORCE_32BIT nos aseguramos de que Econtent se compile a un largo, por lo que es intercambiable con E.
fuente
Si realmente no desea que su enumeración aparezca en su archivo de encabezado Y se asegure de que solo se use por métodos privados, entonces una solución puede ser seguir el principio de pimpl.
Es una técnica que asegura ocultar las partes internas de la clase en los encabezados simplemente declarando:
Luego, en su archivo de implementación (cpp), declara una clase que será la representación de los elementos internos.
Debe crear dinámicamente la implementación en el constructor de la clase y eliminarla en el destructor y al implementar el método público, debe usar:
Hay ventajas para usar pimpl, una es que desacopla el encabezado de su clase de su implementación, no es necesario volver a compilar otras clases al cambiar la implementación de una clase. Otra es que acelera su tiempo de compilación porque sus encabezados son muy simples.
Pero es difícil de usar, por lo que realmente debería preguntarse si declarar su enumeración como privada en el encabezado es un problema.
fuente
Puede envolver la enumeración en una estructura, agregando algunos constructores y conversiones de tipo, y en su lugar declarar la estructura.
Esto parece funcionar: http://ideone.com/TYtP2
fuente
¡Parece que no se puede declarar hacia adelante en GCC!
Interesante discusión aquí
fuente
Hay un poco de desacuerdo ya que esto fue golpeado (más o menos), así que aquí hay algunos bits relevantes del estándar. La investigación muestra que el estándar no define realmente la declaración hacia adelante, ni declara explícitamente que las enumeraciones pueden o no pueden ser declaradas hacia adelante.
Primero, de dcl.enum, sección 7.2:
Por lo tanto, el tipo subyacente de una enumeración está definido por la implementación, con una restricción menor.
A continuación, pasamos a la sección sobre "tipos incompletos" (3.9), que es casi tan cercana como llegamos a cualquier estándar sobre declaraciones a futuro:
Entonces, allí, el estándar presentaba más o menos los tipos que se pueden declarar hacia adelante. Enum no estaba allí, por lo que los autores del compilador generalmente consideran que la declaración no está permitida por el estándar debido al tamaño variable de su tipo subyacente.
Tiene sentido también. Las enumeraciones generalmente se mencionan en situaciones de valor, y el compilador realmente necesitaría saber el tamaño de almacenamiento en esas situaciones. Dado que el tamaño de almacenamiento está definido por la implementación, muchos compiladores pueden optar por usar valores de 32 bits para el tipo subyacente de cada enumeración, momento en el que es posible declararlos hacia adelante. Un experimento interesante podría ser intentar declarar una enumeración en Visual Studio y luego forzarla a usar un tipo subyacente mayor que sizeof (int) como se explicó anteriormente para ver qué sucede.
fuente
Para VC, aquí está la prueba sobre la declaración directa y la especificación del tipo subyacente:
Pero recibí la advertencia para / W4 (/ W3 no incurre en esta advertencia)
advertencia C4480: extensión no estándar utilizada: especificación del tipo subyacente para la enumeración 'T'
VC (Microsoft (R) 32 / bit C / C ++ Optimizing Compiler Version 15.00.30729.01 for 80x86) parece defectuoso en el caso anterior:
El código de ensamblaje anterior se extrae directamente de /Fatest.asm, no es mi suposición personal. ¿Ves el mov DWORD PTR [eax], 305419896; 12345678H línea?
el siguiente fragmento de código lo demuestra:
El resultado es: 0x78, 0x56, 0x34, 0x12
la instrucción clave anterior se convierte en:
mov BYTE PTR [eax], 120; 00000078H
El resultado final es: 0x78, 0x1, 0x1, 0x1
Tenga en cuenta que el valor no se sobrescribe
Por lo tanto, el uso de la declaración directa de enumeración en VC se considera perjudicial.
Por cierto, para no sorprender, la sintaxis para la declaración del tipo subyacente es la misma que en C #. En la práctica, descubrí que vale la pena guardar 3 bytes especificando el tipo subyacente como char cuando se habla con el sistema incorporado, que tiene memoria limitada.
fuente
En mis proyectos, adopté la técnica de enumeración de espacio de nombres para tratar con
enum
s de componentes heredados y de terceros. Aquí hay un ejemplo:adelante.h:
enum.h:
foo.h:
foo.cc:
main.cc:
Tenga en cuenta que el
foo.h
encabezado no tiene que saber nadalegacy::evil
. Solo selegacy::evil
deben incluir los archivos que usan el tipo heredado (aquí: main.cc)enum.h
.fuente
Mi solución a su problema sería:
1 - use int en lugar de enumeraciones: declare sus entradas en un espacio de nombres anónimo en su archivo CPP (no en el encabezado):
Como sus métodos son privados, nadie interferirá con los datos. Incluso podría ir más allá para probar si alguien le envía datos no válidos:
2: cree una clase completa con instancias de const limitadas, como en Java. Reenvíe la clase, y luego defínala en el archivo CPP, e instancia solo los valores de tipo enum. Hice algo así en C ++, y el resultado no fue tan satisfactorio como se deseaba, ya que necesitaba un código para simular una enumeración (construcción de copia, operador =, etc.).
3: Como se propuso anteriormente, use la enumeración privada declarada. A pesar de que un usuario verá su definición completa, no podrá usarlo ni utilizar los métodos privados. Por lo tanto, normalmente podrá modificar la enumeración y el contenido de los métodos existentes sin necesidad de volver a compilar el código con su clase.
Mi conjetura sería la solución 3 o 1.
fuente
Debido a que la enumeración puede ser un tamaño integral de tamaño variable (el compilador decide qué tamaño tiene una enumeración dada), el puntero a la enumeración también puede tener un tamaño variable, ya que es un tipo integral (los caracteres tienen punteros de un tamaño diferente en algunas plataformas por ejemplo).
Por lo tanto, el compilador ni siquiera puede permitirle declarar hacia adelante la enumeración y usar un puntero al usuario, porque incluso allí, necesita el tamaño de la enumeración.
fuente
Defina una enumeración para restringir los posibles valores de elementos del tipo a un conjunto limitado. Esta restricción se aplicará en tiempo de compilación.
Cuando se declara hacia adelante el hecho de que usará un 'conjunto limitado' más adelante no agrega ningún valor: el código posterior necesita conocer los valores posibles para beneficiarse de él.
Aunque al compilador le preocupa el tamaño del tipo enumerado, la intención de la enumeración se pierde cuando la reenvía.
fuente