Estou tentando fazer algo como o seguinte:
enum E;
void Foo(E e);
enum E {A, B, C};
que o compilador rejeita. Eu dei uma olhada rápida no Google e o consenso parece ser "você não pode fazer isso", mas não consigo entender o porquê. Alguém pode explicar?
Esclarecimento 2: Estou fazendo isso porque tenho métodos particulares em uma classe que aceita o enum e não quero que os valores do enum sejam expostos - portanto, por exemplo, não quero que ninguém saiba que E é definido como
enum E {
FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}
como o projeto X não é algo que eu quero que meus usuários saibam.
Então, eu queria encaminhar declarar a enum para poder colocar os métodos privados no arquivo de cabeçalho, declarar a enum internamente no cpp e distribuir o arquivo e o cabeçalho da biblioteca criada para as pessoas.
Quanto ao compilador - é o GCC.
Respostas:
O motivo pelo qual a enum não pode ser declarada para frente é que, sem conhecer os valores, o compilador não pode conhecer o armazenamento necessário para a variável enum. O C ++ Compiler pode especificar o espaço de armazenamento real com base no tamanho necessário para conter todos os valores especificados. Se tudo o que é visível é a declaração de encaminhamento, a unidade de tradução não pode saber qual tamanho de armazenamento foi escolhido - pode ser um caractere, um int ou qualquer outra coisa.
Da seção 7.2.5 da norma ISO C ++:
Como o chamador da função deve conhecer os tamanhos dos parâmetros para configurar corretamente a pilha de chamadas, o número de enumerações em uma lista de enumerações deve ser conhecido antes do protótipo da função.
Atualização: No C ++ 0X, uma sintaxe para declarar tipos de enum previamente foi proposta e aceita. Você pode ver a proposta em http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
fonte
struct S; void foo(S s);
(observe quefoo
é declarado apenas, não definido), não há razão para que não possamos fazerenum E; void foo(E e);
o mesmo. Nos dois casos, o tamanho não é necessário.A declaração direta de enumerações é possível desde o C ++ 11. Anteriormente, a razão pela qual os tipos de enum não podiam ser declarados para frente é porque o tamanho da enumeração depende de seu conteúdo. Desde que o tamanho da enumeração seja especificado pelo aplicativo, ele pode ser declarado adiante:
fonte
Estou adicionando uma resposta atualizada aqui, dados os desenvolvimentos recentes.
Você pode declarar uma enumeração avançada no C ++ 11, desde que declare seu tipo de armazenamento ao mesmo tempo. A sintaxe é assim:
De fato, se a função nunca se referir aos valores da enumeração, você não precisará da declaração completa nesse momento.
Isso é suportado pelo G ++ 4.6 e posteriores (
-std=c++0x
ou-std=c++11
em versões mais recentes). O Visual C ++ 2013 suporta isso; nas versões anteriores, ele tem algum tipo de suporte não padrão que ainda não descobri - encontrei algumas sugestões de que uma simples declaração de encaminhamento é legal, mas o YMMV.fonte
enum class
como uma extensão C ++ (antes de C ++ 11 ser diferenteenum class
), pelo menos se bem me lembro. O compilador permitiu que você especifique o tipo subjacente de uma enumeração, mas não ofereceu suporteenum class
ou enumerações declaradas a frente, e avisou que qualificar um enumerador com o escopo da enumeração era uma extensão não padrão. Lembro-me de trabalhar aproximadamente da mesma forma que especificar o tipo subjacente no C ++ 11, exceto mais irritante porque você teve que suprimir o aviso.A declaração de encaminhamento de itens no C ++ é muito útil porque acelera drasticamente o tempo de compilação . Você pode declarar frente várias coisas em C ++, incluindo:
struct
,class
,function
, etc ...Mas você pode encaminhar declarar um
enum
em C ++?Não, você não pode.
Mas por que não permitir? Se fosse permitido, você poderia definir seu
enum
tipo no seu arquivo de cabeçalho e seusenum
valores no seu arquivo de origem. Parece que isso deve ser permitido, certo?Errado.
No C ++, não existe um tipo padrão para o
enum
C # (int). Em C ++, seuenum
tipo será determinado pelo compilador como qualquer tipo que caiba no intervalo de valores que você possui para o seuenum
.O que isso significa?
Isso significa que o
enum
tipo subjacente do seu não pode ser totalmente determinado até que você tenha todos os valores doenum
definido. Qual homem você não pode separar a declaração e a definição da suaenum
. E, portanto, você não pode encaminhar declarar umenum
em C ++.O padrão ISO C ++ S7.2.5:
Você pode determinar o tamanho de um tipo enumerado em C ++ usando o
sizeof
operador O tamanho do tipo enumerado é o tamanho do seu tipo subjacente. Dessa forma, você pode adivinhar qual o tipo que seu compilador está usando para o seuenum
.E se você especificar o tipo do seu
enum
explicitamente assim:Você pode então declarar o seu
enum
?Não. Mas por que não?
A especificação do tipo de um
enum
não faz parte do padrão C ++ atual. É uma extensão VC ++. No entanto, fará parte do C ++ 0x.Fonte
fonte
[Minha resposta está errada, mas deixei aqui porque os comentários são úteis].
Enums de declaração de encaminhamento não são padrão, porque não é garantido que ponteiros para diferentes tipos de enum sejam do mesmo tamanho. O compilador pode precisar ver a definição para saber qual ponteiro de tamanho pode ser usado com esse tipo.
Na prática, pelo menos em todos os compiladores populares, os ponteiros para enumerações são um tamanho consistente. A declaração direta de enumerações é fornecida como uma extensão de idioma pelo Visual C ++, por exemplo.
fonte
De fato, não existe uma declaração de enum. Como a definição de uma enum não contém nenhum código que possa depender de outro código usando a enum, geralmente não é um problema defini-la completamente quando você a declara pela primeira vez.
Se o único uso de sua enumeração for por funções membro privadas, você poderá implementar o encapsulamento tendo a enumeração como membro privado dessa classe. A enum ainda precisa ser totalmente definida no ponto da declaração, ou seja, dentro da definição de classe. No entanto, esse não é um problema maior, pois declarar funções de membro privadas lá, e não é uma exposição pior de componentes internos de implementação do que isso.
Se você precisar de um grau mais profundo de ocultação para os detalhes da implementação, poderá transformá-lo em uma interface abstrata, consistindo apenas em funções virtuais puras e em uma classe concreta, completamente oculta, implementando (herdando) a interface. A criação de instâncias de classe pode ser manipulada por uma fábrica ou por uma função de membro estática da interface. Dessa forma, mesmo o nome real da classe, sem falar em suas funções particulares, não será exposto.
fonte
Apenas observando que o motivo é que o tamanho da enum ainda não é conhecido após a declaração encaminhada. Bem, você usa a declaração direta de uma estrutura para poder passar um ponteiro ou se referir a um objeto de um local mencionado na própria definição de estrutura declarada direta também.
Encaminhar a declaração de uma enumeração não seria muito útil, pois seria possível transmitir o valor da enumeração. Você não podia nem ter um ponteiro para isso, porque recentemente me disseram que algumas plataformas usam ponteiros de tamanho diferente para char do que para int ou long. Portanto, tudo depende do conteúdo da enumeração.
O padrão C ++ atual proíbe explicitamente fazer algo como
(dentro
7.1.5.3/1
). Mas o próximo padrão C ++, previsto para o próximo ano, permite o seguinte, o que me convenceu de que o problema realmente tem a ver com o tipo subjacente:É conhecida como uma declaração enum "opaca". Você pode até usar X por valor no código a seguir. E seus enumeradores podem ser definidos posteriormente em uma redeclaração posterior da enumeração. Veja
7.2
no rascunho de trabalho atual.fonte
Eu faria assim:
[no cabeçalho público]
[no cabeçalho interno]
Ao adicionar FORCE_32BIT, garantimos que o Econtent seja compilado por muito tempo, para que seja intercambiável com E.
fonte
Se você realmente não deseja que seu enum apareça no arquivo de cabeçalho E garanta que ele seja usado apenas por métodos particulares, uma solução pode ser seguir o princípio pimpl.
É uma técnica que garante ocultar as classes internas nos cabeçalhos, apenas declarando:
Então, no seu arquivo de implementação (cpp), você declara uma classe que será a representação dos internos.
Você deve criar dinamicamente a implementação no construtor da classe e excluí-la no destruidor e, ao implementar o método público, você deve usar:
Existem profissionais para usar o pimpl, um é que ele desacopla o cabeçalho da sua classe da sua implementação, não há necessidade de recompilar outras classes ao alterar a implementação de uma classe. Outra é que isso acelera seu tempo de compilação porque seus cabeçalhos são muito simples.
Mas é difícil usar, então você deve se perguntar se apenas declarar sua enumeração como privada no cabeçalho é um problema.
fonte
Você pode agrupar a enum em uma estrutura, adicionando alguns construtores e digitar conversões e, em vez disso, declarar a estrutura.
Isso parece funcionar: http://ideone.com/TYtP2
fonte
Parece que não pode ser declarado para a frente no GCC!
Discussão interessante aqui
fonte
Há alguma discordância desde que isso foi causado (mais ou menos), então aqui estão alguns bits relevantes do padrão. A pesquisa mostra que o padrão não define realmente a declaração de encaminhamento, nem declara explicitamente que as enums podem ou não podem ser declaradas como encaminhadas.
Primeiro, no dcl.enum, seção 7.2:
Portanto, o tipo subjacente de um enum é definido pela implementação, com uma restrição menor.
A seguir, passamos à seção "tipos incompletos" (3.9), que é o mais próximo que chegamos a qualquer padrão nas declarações avançadas:
Então, lá, o padrão praticamente expôs os tipos que podem ser declarados adiante. O enum não estava lá; portanto, os autores do compilador geralmente consideram a declaração encaminhada como não permitida pelo padrão devido ao tamanho da variável do tipo subjacente.
Também faz sentido. As enums geralmente são referenciadas em situações de valor, e o compilador precisaria realmente saber o tamanho do armazenamento nessas situações. Como o tamanho do armazenamento é definido pela implementação, muitos compiladores podem optar por usar valores de 32 bits para o tipo subjacente de cada enumeração, quando é possível encaminhar a declaração. Um experimento interessante pode ser tentar declarar uma enumeração no visual studio, forçando-a a usar um tipo subjacente maior que sizeof (int), conforme explicado acima, para ver o que acontece.
fonte
Para VC, aqui está o teste sobre a declaração direta e a especificação do tipo subjacente:
Mas recebi o aviso para / W4 (/ W3 não incorre nesse aviso)
aviso C4480: extensão não padrão usada: especificando o tipo subjacente para a enumeração 'T'
O VC (Microsoft (R) C / C ++ Optimizer Compiler versão 15.00.30729.01 para 80x86) parece com erros no caso acima:
O código de montagem acima é extraído diretamente do /Fatest.asm, não do meu palpite pessoal. Você vê o mov DWORD PTR [eax], 305419896; Linha 12345678H?
o seguinte trecho de código prova:
o resultado é: 0x78, 0x56, 0x34, 0x12
a instrução principal acima se torna:
mov BYTE PTR [eax], 120; 00000078H
o resultado final é: 0x78, 0x1, 0x1, 0x1
Observe que o valor não está sendo substituído
Portanto, o uso da declaração direta de enum no VC é considerado prejudicial.
BTW, para não surpreender, a sintaxe para declaração do tipo subjacente é a mesma que em C #. Na prática, achei que vale a pena economizar 3 bytes, especificando o tipo subjacente como char quando falar com o sistema incorporado, que é limitado por memória.
fonte
Nos meus projetos, adotei a técnica de enumeração vinculada a namespace para lidar com
enum
s de componentes herdados e de terceiros. Aqui está um exemplo:forward.h:
enum.h:
foo.h:
foo.cc:
main.cc:
Observe que o
foo.h
cabeçalho não precisa saber nada sobrelegacy::evil
. Somente os arquivos que usam o tipo herdadolegacy::evil
(aqui: main.cc) precisam ser incluídosenum.h
.fonte
Minha solução para o seu problema seria:
1 - use int em vez de enumerações: declare suas entradas em um espaço para nome anônimo no seu arquivo CPP (não no cabeçalho):
Como seus métodos são particulares, ninguém mexerá com os dados. Você pode testar ainda mais se alguém lhe envia dados inválidos:
2: crie uma classe completa com instanciações const limitadas, como em Java. Encaminhar declare a classe e, em seguida, defina-a no arquivo CPP e instancie apenas os valores semelhantes a enum. Fiz algo assim em C ++, e o resultado não foi tão satisfatório quanto o desejado, pois era necessário algum código para simular uma enumeração (construção de cópia, operador =, etc.).
3: Como proposto anteriormente, use o enum declarado em particular. Apesar de um usuário ver sua definição completa, ele não poderá usá-lo nem usar métodos privados. Portanto, você geralmente poderá modificar a enumeração e o conteúdo dos métodos existentes sem precisar recompilar o código usando sua classe.
Meu palpite seria a solução 3 ou 1.
fonte
Como o enum pode ser um tamanho integral de tamanho variável (o compilador decide qual tamanho um determinado enum possui), o ponteiro para o enum também pode ter tamanho variável, já que é um tipo integral (caracteres possuem ponteiros de tamanho diferente em algumas plataformas por exemplo).
Portanto, o compilador não pode nem mesmo permitir que você declare a enum e use um ponteiro para ele, porque mesmo lá, ele precisa do tamanho da enum.
fonte
Você define uma enumeração para restringir os possíveis valores de elementos do tipo a um conjunto limitado. Essa restrição deve ser aplicada no momento da compilação.
Ao declarar adiante o fato de que você usará um 'conjunto limitado' posteriormente, não adiciona nenhum valor: o código subsequente precisa conhecer os valores possíveis para se beneficiar dele.
Embora o compilador esteja preocupado com o tamanho do tipo enumerado, a intenção da enumeração se perde quando você o declara adiante.
fonte