Pregunta

Estoy tratando de hacer algo como lo siguiente:

enum E;

void Foo(E e);

enum E {A, B, C};

que el compilador rechaza.He tenido un rápido vistazo en Google y el consenso parece ser "no se puede hacer", pero no puedo entender por qué.¿Alguien puede explicar?

Aclaración 2:Estoy haciendo esto como he privado de los métodos en una clase que tome dijo enum, y no quiero que la enumeración de los valores expuestos, así, por ejemplo, no quiero que nadie sepa que E se define como

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

como proyecto X no es algo que yo quiero que mis usuarios para saber acerca de.

Así que, quería adelante declarar la enumeración para que yo pudiera poner los métodos privados en el archivo de encabezado, declarar la enumeración internamente en el cpp, y distribuir el integrado de la biblioteca y archivo de encabezado a las personas.

Como para el compilador - es GCC.

¿Fue útil?

Solución

La razón de la enumeración no puede ser hacia adelante declarado es que sin el conocimiento de los valores, el compilador no puede saber el almacenamiento necesario para la variable enum.Compilador de C++ se permite especificar el espacio de almacenamiento basado en el tamaño necesario para contener todos los valores especificados.Si todo lo que es visible es la declaración forward, la unidad de traducción no puede saber lo que el tamaño de almacenamiento se han elegido - que podría ser un char o int, o algo más.


De la Sección 7.2.5 de la ISO Estándar de C++:

El tipo subyacente de una enumeración es un tipo integral que puede representar todos los enumerador de valores definidos en la enumeración.Es de aplicación definido qué tipo integral, es utilizado como el tipo subyacente de una enumeración, salvo que el tipo subyacente no deberá ser mayor que int a menos que el valor de un enumerador no caben en un int o unsigned int.Si el el enumerador de la lista está vacío, el tipo subyacente es como si la enumeración había una sola enumerador con el valor 0.El valor de sizeof() aplicado a un tipo de enumeración, un objeto de tipo enumeración, o un enumerador, es el valor de sizeof() aplica para el tipo subyacente.

Desde el llamador a la función que deben saber 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 deben ser conocidos antes de que el prototipo de la función.

Actualización:En C++0X una sintaxis para la introduccion de declarar los tipos de enumeración ha sido propuesto y aceptado.Usted puede ver la propuesta en http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf

Otros consejos

Adelante declaración de las enumeraciones también es posible en C++0x.Anteriormente, la razón por la enumeración de los tipos no se pudo avanzar declarado es debido a que el tamaño de la enumeración depende de su contenido.Mientras el tamaño de la enumeración es especificado por la aplicación, puede ser hacia adelante declaró:

enum Enum1;                   //Illegal in C++ and C++0x; no size is explicitly specified.
enum Enum2 : unsigned int;    //Legal in C++0x.
enum class Enum3;             //Legal in C++0x, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; //Legal C++0x.
enum Enum2 : unsigned short;  //Illegal in C++0x, because Enum2 was previously declared with a different type.

Voy a agregar un up-to-fecha de la respuesta aquí, en vista de los recientes acontecimientos.

Puede reenviar a declarar una enumeración en C++11, siempre y cuando usted puede declarar su tipo de almacenamiento al mismo tiempo.La sintaxis se parece a esto:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

De hecho, si la función nunca se refiere a los valores de la enumeración, no es necesario la declaración completa en todos en ese momento.

Esto es apoyado por G++ 4.6 y en adelante (-std=c++0x o -std=c++11 en las versiones más recientes).Visual C++ 2013 es compatible con este;en versiones anteriores se tiene algún tipo de no-estándar de apoyo que no he averiguado todavía - he encontrado alguna sugerencia de que una simple declaración forward es legal, pero YMMV.

Adelante declarando cosas en C++ es muy útil, ya que acelera drásticamente el tiempo de compilación.Puede reenviar declarar varias cosas en C++, incluyendo: struct, class, function, etc...

Pero se puede avanzar declarar una enum en C++?

No, No puedes.

Pero, ¿por qué no lo permite?Si se dejaba podría definir su enum escriba en el archivo de encabezado, y su enum los valores en el archivo de origen.Suena como que debe ser permitido a la derecha?

Mal.

En C++ no hay ningún tipo por defecto para enum como la hay en C# (int).En C++ de su enum tipo será determinado por el compilador para ser de cualquier tipo que se ajuste el rango de valores que tiene para su enum.

¿Qué significa eso?

Esto significa que su enum's tipo subyacente no puede ser determinado hasta que no tengas todos los valores de la enum definido.Que hombre no se puede separar de la declaración y la definición de su enum.Y por lo tanto no puede reenviar declarar una enum en C++.

La ISO C++ estándar de S7.2.5:

El tipo subyacente de una enumeración es un tipo integral que puede representar todos los enumerador de valores definidos en la enumeración.Es de aplicación definido qué tipo integral, es utilizado como el tipo subyacente de una enumeración, salvo que el tipo subyacente no deberá ser mayor que int a menos que el valor de un enumerador no caben en un int o unsigned int.Si el enumerador de la lista está vacía, el tipo subyacente es como si la enumeración había una sola enumerador con el valor 0.El valor de sizeof() aplicado a un tipo de enumeración, un objeto de tipo enumeración, o un enumerador, es el valor de sizeof() aplica para el tipo subyacente.

Usted puede determinar el tamaño de un tipo enumerado en C++ con el sizeof operador.El tamaño del tipo enumerado es el tamaño de su tipo subyacente.De esta manera se puede adivinar que tipo de su compilador es el uso de su enum.

Lo que si puede especificar el tipo de su enum explícitamente como este:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Se puede entonces adelante declarar su enum?

No.Pero, ¿por qué no?

Especificar el tipo de un enum no es parte de la realidad actual estándar de C++.Se trata de un VC++ extensión.Será parte de C++0x, aunque.

Fuente

[Mi respuesta es incorrecta, pero lo he dejado aquí porque los comentarios son útiles].

Adelante declarando las enumeraciones no es estándar, ya que los punteros a los diferentes tipos de enumeración no están garantizados para ser del mismo tamaño.El compilador puede que necesite ver la definición para saber lo que el tamaño de los punteros se pueden utilizar con este tipo.

En la práctica, al menos en todos los populares de los compiladores, los punteros a las enumeraciones son de un tamaño uniforme.Adelante declaración de las enumeraciones se proporciona como una extensión del lenguaje de Visual C++, por ejemplo.

En realidad, no existe tal cosa como un avance declaración de enum.Como una enumeración de la definición no contiene ningún código que podría depender de otro código mediante la enumeración, que no suele ser un problema para definir el enum completamente cuando eres el primero declara.

Si el sólo uso de su enumeración es privado por funciones miembro, usted puede implementar la encapsulación por tener la enumeración a sí mismo como un miembro privado de esa clase.La enumeración aún no se ha definido por completo en el momento de la declaración, es decir, dentro de la definición de la clase.Sin embargo, esto no es un problema mayor como la de declarar miembro privado de funciones, y no es peor exposal de aplicación interna que eso.

Si usted necesita un grado más profundo de la ocultación de los detalles de la implementación, se pueden romper en una interfaz abstracta, sólo consta de las funciones virtuales puras, y de manera concreta, completamente oculta, de la clase de aplicación (heredar) de la interfaz.La creación de instancias de la clase puede ser manejado por una fábrica o en una función miembro estática de la interfaz.De esa manera, incluso el verdadero nombre de la clase, por no hablar de su funciones privadas, no van a ser expuestos.

Sólo señalar que la razón por la realidad es que el tamaño de la enumeración no es aún conocido, después de la declaración forward.Así, se utiliza adelante declaración de una estructura de poder pasar un puntero o referencia a un objeto de un lugar al que nos referimos en adelante declaró la estructura de definición de sí mismo también.

Adelante declarar una enumeración no sería demasiado útil, porque a uno le gustaría ser capaz de pasar todo el enum por valor.No se podía incluso tener un puntero a él, porque recientemente me contó algunas plataformas de uso de punteros de diferentes tamaños para char que para int o long.Así que todo depende del contenido de la enumeración.

El actual Estándar de C++ explícitamente prohibe hacer algo como

enum X;

(en 7.1.5.3/1).Pero el próximo Estándar de C++ debido a que el próximo año permite a los siguientes, que me convenció de que el problema en realidad ha que ver con el tipo subyacente:

enum X : int;

Esto se conoce como una "opaco" declaración enum.Usted puede incluso utilizar X por valor de en el siguiente código.Y su enumeradores más tarde puede ser definido más adelante en un redeclaration de la enumeración.Ver 7.2 en el actual borrador de trabajo.

Yo lo haría de esta manera:

[en el encabezado público]

typedef unsigned long E;

void Foo(E e);

[en la cabecera interna]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

Mediante la adición de FORCE_32BIT nos aseguramos de que Econtent se compila a un largo, así que es intercambiable con E.

Parece que no puede ser hacia adelante-declaró en GCC!

Interesante debate aquí

Si usted realmente no quiere que su enumeración a aparecer en el encabezado del archivo Y asegúrese de que sólo es utilizado por los métodos privados, a continuación, una solución puede ser ir con el pimpl principio.

Es una técnica que asegurar para ocultar la clase interna en los encabezados de simplemente declarar:

class A 
{
public:
    ...
private:
    void* pImpl;
};

A continuación, en el archivo de implementación (cpp), puede declarar una clase que será la representación de la información interna.

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

Usted debe crear dinámicamente la aplicación en el constructor de la clase y eliminar en el destructor y cuando la aplicación de un método público, debe utilizar:

((AImpl*)pImpl)->PrivateMethod();

Hay ventajas para el uso de pimpl, una de ellas es que se desvincule de su clase encabezado de su aplicación, sin necesidad de volver a compilar otras clases cuando el cambio de una implementación de la clase.Otra es que se acelera el tiempo de compilación debido a que los encabezados son tan simples.

Pero es un dolor de uso, por lo que realmente debería preguntarse si acaba de declarar su enum como privado en el encabezado es que mucho de los problemas.

Usted puede envolver la enumeración en una estructura, añadiendo en algunos constructores y conversiones de tipo, de avance y de declarar que la estructura en su lugar.

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

Esto parece funcionar:http://ideone.com/TYtP2

Hay algunas disidencia desde este he topado (más o menos), así que aquí están algunos relevante bits de la norma.La investigación muestra que la norma no se realmente que definan el futuro de la declaración, ni tampoco se establece explícitamente que las enumeraciones pueden o no pueden ser hacia adelante declaró.

En primer lugar, a partir de dcl.enum, sección 7.2:

El tipo subyacente de una enumeración es un tipo integral que puede representar todo el enumerador de valores definidos en la enumeración.Es la aplicación definida por el que integral se utiliza el tipo como el tipo subyacente para una enumeración, salvo que el tipo subyacente no deberá ser mayor de int a menos que el valor de un enumerador no caben en un int o unsigned int.Si el enumerador de la lista está vacío, el tipo subyacente es como si la enumeración había una sola enumerador con el valor 0.El valor de sizeof() aplicado a una enumeración el tipo, un objeto de tipo enumeración, o un enumerador, es el valor de sizeof() aplicado a la base tipo.

Así que el tipo subyacente de una enumeración es de aplicación definido, con una menor restricción.

Lo siguiente que voltear a la sección de "incompleto" tipos de (3.9), que es lo más cerca que podemos llegar a cualquier estándar en adelante declaraciones:

Una clase que ha sido declarado pero no definido, o una matriz de tamaño desconocido o de incompleta tipo de elemento de, es un incompletamente definidos por el tipo de objeto.

Un tipo de clase (tales como "clase X") podría ser incompleta en uno de los puntos de traducción unidad y completar más adelante;el tipo de "clase X" es el mismo tipo en ambos puntos.El declarado el tipo de un objeto de matriz podría ser una matriz de incompleta tipo de clase y por lo tanto incompleta;si la clase es de tipo completado más tarde, en la unidad de traducción, el tipo de matriz que se completa;el tipo de matriz en esos dos puntos es el mismo tipo.El tipo declarado de una matriz de objetos podría ser una matriz de tamaño desconocido y por lo tanto ser incompleta en un punto en una unidad de traducción y completa más adelante;los tipos de matriz en los dos puntos ("matriz de desconocido obligado de T" y la "matriz de N T") son diferentes tipos.El tipo de un puntero a una matriz de tamaño desconocido, o de un tipo definido por un typedef la declaración de un array de tamaño desconocido, no puede ser completado.

Así, la norma bastante mucho puesto que los tipos que puede ser hacia adelante, declaró.Enum no estaba allí, así que el compilador de los autores en general respecto de avance de declarar como no permitido por la norma, debido a que la variable tamaño de su tipo subyacente.

Y tiene sentido.Las enumeraciones son usualmente se hace referencia en el valor de las situaciones, y el compilador de verdad se necesita saber el tamaño de almacenamiento en esas situaciones.Dado que el tamaño de almacenamiento es de aplicación definidos, muchos compiladores sólo puede elegir el uso de 32 bits de los valores para el tipo subyacente de cada enum, momento en el cual se hace posible declarar hacia delante de ellos.Un experimento interesante sería tratar de avanzar declarar una enumeración en visual studio, lo que obligó a utilizar un tipo subyacente mayor que sizeof(int), como se explicó anteriormente, a ver qué pasa.

Para VC, aquí está la prueba acerca de la declaración forward y especificar el tipo subyacente:

  1. el siguiente código es compilado aceptar.
    typedef int myint;
    enum T ;
    void foo(T * tp )
    {
        * tp = (T)0x12345678;
    }
    enum T : char
    {
        A
    };

Pero tengo la advertencia para los /W4(/W3 no incurrir en este aviso)

advertencia C4480:no estándar de la extensión:especificar tipo subyacente para enum 'T'

  1. VC(Microsoft (R) de 32 bits de C/C++ Compilador de Optimización de la Versión 15.00.30729.01 para 80x86) se ve buggy en el caso anterior:

    • al ver enum T;VC supone que el tipo enum T usa por defecto 4 bytes int como tipo subyacente, por lo que el ensamblado generado el código es:
    ?foo@@YAXPAW4T@@@Z PROC                 ; foo
    ; File e:\work\c_cpp\cpp_snippet.cpp
    ; Line 13
        push    ebp
        mov ebp, esp
    ; Line 14
        mov eax, DWORD PTR _tp$[ebp]
        mov DWORD PTR [eax], 305419896      ; 12345678H
    ; Line 15
        pop ebp
        ret 0
    ?foo@@YAXPAW4T@@@Z ENDP                 ; foo

El anterior código de ensamblado se extrae de /Fatest.asm directamente, no a mi personal, supongo.¿Ve usted la mov DWORD PTR[eax], 305419896 ;12345678H línea?

el fragmento de código siguiente lo demuestra:

    int main(int argc, char *argv)
    {
        union {
            char ca[4];
            T t;
        }a;
        a.ca[0] = a.ca[1] = a.[ca[2] = a.ca[3] = 1;
        foo( &a.t) ;
        printf("%#x, %#x, %#x, %#x\n",  a.ca[0], a.ca[1], a.ca[2], a.ca[3] );
        return 0;
    }

el resultado es:0x78, 0x56, 0x34, 0 x 12

  • después de quitar el avance de la declaración de enum T y mover la definición de la función foo después de la enumeración T definición:el resultado es OK:

la clave anterior instrucción se convierte en:

mov BYTE PTR [eax], 120 ;00000078H

el resultado final es:0x78, 0x1, 0 x 1, 0 x 1

Nota el valor no se sobrescriben

Así que el uso de la declaración de enum en VC se considera perjudicial.

Por CIERTO, que no es de extrañar, la sintaxis para la declaración de que el tipo subyacente es la misma como en C#.En la práctica he encontrado es que vale la pena guardar 3 bytes especificando el tipo subyacente como char cuando hable con el sistema embebido, que la memoria es limitada.

En mis proyectos, que aprobó la Espacio De Nombres Enlazados A La Enumeración técnica para lidiar con enums de legado y la 3ª parte de los componentes.He aquí un ejemplo:

adelante.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

Tenga en cuenta que el foo.h encabezado no tiene que saber nada acerca de legacy::evil.Sólo los archivos que uso el legado tipo legacy::evil (aquí:main.cc) necesidad de incluir enum.h.

Mi solución a tu problema sería de:

1 - el uso de int en lugar de las enumeraciones:Declarar su enteros en un anónimo espacio de nombres en el archivo CPP (no en el encabezado):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

Como sus métodos son privadas, nadie va a meterse con los datos.Usted podría ir incluso más lejos para probar si alguien le envía un datos no válidos:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2 :crear una clase completa con limitada const casos, como el hecho en Java.Adelante declarar la clase y, a continuación, defina en el archivo CPP, y a instancias sólo la enumeración como valores.Yo hice algo parecido en C++, y el resultado no fue tan satisfactorio como se desee, ya que necesitaba un poco de código para simular un enum (copia de la construcción, operador =, etc.).

3 :Como propuesto antes, el uso de la privada declarada de enumeración.A pesar de el hecho de que un usuario ve su definición completa, no será capaz de utilizarlo, ni tampoco el uso de los métodos privados.Así que, por lo general, ser capaz de modificar la enumeración y el contenido de los métodos existentes sin necesidad de volver a compilar el código de la utilización de su clase.

Mi conjetura sería la solución 3 o 1.

Debido a que la enumeración puede ser una parte integral de tamaño de tamaño variable (el compilador decide qué tamaño de una enumeración dado cuenta), el puntero a la enumeración también puede tener distinto tamaño, ya que es un tipo integral (caracteres de los punteros de un tamaño diferente en algunas plataformas, por ejemplo).

Por lo que el compilador no puede incluso permitir que usted hacia adelante-declarar el enum de usuario y un puntero a ella, porque incluso allí, se necesita el tamaño de la enumeración.

Definir una enumeración para restringir los posibles valores de los elementos del tipo a un conjunto limitado.Esta restricción se aplica en tiempo de compilación.

Cuando adelante declarando el hecho de que usted va a utilizar un 'conjunto limitado' posteriormente no agrega valor:código posteriores necesita conocer los valores posibles con el fin de beneficiarse de ella.

Aunque el compilador es preocupados por el tamaño del tipo enumerado, el la intención de la enumeración se pierde cuando se adelante a declarar.

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