Encaminhar declarando uma enumeração em C ++

265

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.

szevvy
fonte
Há muitos anos e, de alguma forma, o StackOverflow me atraiu de volta;) Como sugestão post-mortem - não faça isso especialmente no cenário que você descreve. Eu preferiria definir uma interface abstrata e expor isso aos usuários e manter a definição de enumeração e todos os outros detalhes da implementação com a implementação interna que ninguém mais vê do meu lado, permitindo que eu faça o que for quando e tenha controle total sobre quando os usuários verão qualquer coisa.
RnR

Respostas:

217

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 ++:

O tipo subjacente de uma enumeração é um tipo integral que pode representar todos os valores do enumerador definidos na enumeração. É definido pela implementação qual tipo integral é usado como o tipo subjacente para uma enumeração, exceto que o tipo subjacente não deve ser maior que int, a menos que o valor de um enumerador não possa caber em um intou unsigned int. Se a lista de enumeradores estiver vazia, o tipo subjacente é como se a enumeração tivesse um único enumerador com valor 0. O valor de sizeof()aplicado a um tipo de enumeração, um objeto de tipo de enumeração ou um enumerador, é o valor sizeof()aplicado a tipo subjacente.

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

KJAWolf
fonte
29
-1. Seu raciocínio não pode estar correto - caso contrário, por que você tem permissão para declarar "classe C"; e, em seguida, declare um protótipo de função que aceita ou retorna um C, antes de definir totalmente C?
j_random_hacker
112
@j_random: Você não pode usar uma classe antes que ela esteja totalmente definida - você só pode usar um ponteiro ou uma referência a essa classe e isso ocorre porque seus tamanhos e formas de operação não dependem do que a classe é.
RnR
27
O tamanho de uma referência ou ponteiro para um objeto de classe é definido pelo compilador e independente do tamanho real do objeto - é o tamanho de ponteiros e referências. A enumeração é um objeto e seu tamanho é necessário para o compilador acessar o armazenamento correto.
KJAWolf 19/04/09
16
Logicamente, seria capaz de declarar ponteiros / referências a enumerações se tivéssemos enumerações de declaração direta, assim como podemos fazer com as classes. É só que você não costuma lidar com ponteiros para enums :)
Pavel Minaev
20
Eu sei que essa discussão terminou há muito tempo, mas tenho que me alinhar com @j_random_hacker aqui: o problema aqui não é sobre ponteiro ou referência a tipos incompletos, mas sobre o uso de tipos incompletos em declarações. Como é legal fazer struct S; void foo(S s);(observe que fooé declarado apenas, não definido), não há razão para que não possamos fazer enum E; void foo(E e);o mesmo. Nos dois casos, o tamanho não é necessário.
precisa
200

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:

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.
user119017
fonte
1
Existe algum suporte do compilador para esse recurso? Parece que o GCC 4.5 não o possui :(
rubenvb
4
@rubenvb O mesmo acontece com o Visual C ++ 11 (2012) blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
knatten
Eu estava procurando por enum32_t e com sua resposta enum XXX: uint32_t {a, b, c};
fantastory
Eu pensei que enums com escopo (classe enum) foram implementados em C ++ 11? Se sim, como eles são legais em C ++ 0X?
Terrabits
1
C ++ 0x era o nome de trabalho do C ++ 11, @Terrabits, antes de ser oficialmente padronizado. A lógica é que, se for conhecido (ou altamente provável) que um recurso seja incluído em um padrão atualizado, o uso desse recurso antes do lançamento oficial do padrão tenderá a usar o nome de trabalho. (Por exemplo, compiladores que deram suporte aos recursos do C ++ 11 antes da padronização oficial em 2011 tiveram suporte ao C ++ 0x, compiladores que deram suporte a recursos do C ++ 17 antes da padronização oficial tiveram suporte ao C ++ 1z e compiladores que suportam os recursos do C ++ 20 agora (2019) têm suporte a C ++ 2a.)
Justin Time - Reinstate Monica
79

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:

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

....

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

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++0xou -std=c++11em 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.

Tom
fonte
4
+1 porque esta é a única resposta que menciona que você precisa declarar o tipo em sua declaração e sua definição.
Turoni
Eu acredito que o suporte parcial no MSVC inicial foi suportado a partir de C ++ / CLI enum classcomo uma extensão C ++ (antes de C ++ 11 ser diferente enum class), pelo menos se bem me lembro. O compilador permitiu que você especifique o tipo subjacente de uma enumeração, mas não ofereceu suporte enum classou 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.
Justin Time - Restabelece Monica
30

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 enumem C ++?

Não, você não pode.

Mas por que não permitir? Se fosse permitido, você poderia definir seu enumtipo no seu arquivo de cabeçalho e seus enumvalores no seu arquivo de origem. Parece que isso deve ser permitido, certo?

Errado.

No C ++, não existe um tipo padrão para o enumC # (int). Em C ++, seu enumtipo será determinado pelo compilador como qualquer tipo que caiba no intervalo de valores que você possui para o seu enum.

O que isso significa?

Isso significa que o enumtipo subjacente do seu não pode ser totalmente determinado até que você tenha todos os valores do enumdefinido. Qual homem você não pode separar a declaração e a definição da sua enum. E, portanto, você não pode encaminhar declarar um enumem C ++.

O padrão ISO C ++ S7.2.5:

O tipo subjacente de uma enumeração é um tipo integral que pode representar todos os valores do enumerador definidos na enumeração. É definido pela implementação qual tipo integral é usado como o tipo subjacente para uma enumeração, exceto que o tipo subjacente não deve ser maior que int, a menos que o valor de um enumerador não possa caber em um intou unsigned int. Se a lista de enumeradores estiver vazia, o tipo subjacente é como se a enumeração tivesse um único enumerador com valor 0. O valor de sizeof()aplicado a um tipo de enumeração, um objeto de tipo de enumeração ou um enumerador, é o valor sizeof()aplicado a tipo subjacente.

Você pode determinar o tamanho de um tipo enumerado em C ++ usando o sizeofoperador 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 seu enum.

E se você especificar o tipo do seu enumexplicitamente assim:

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

Você pode então declarar o seu enum?

Não. Mas por que não?

A especificação do tipo de um enumnão faz parte do padrão C ++ atual. É uma extensão VC ++. No entanto, fará parte do C ++ 0x.

Fonte

Brian R. Bondy
fonte
15
Esta resposta está agora vários anos desatualizada.
Tom
O tempo faz de todos nós tolos. Seu comentário agora está desatualizado há vários anos; a resposta uma década!
pjcard 18/02/19
14

[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.

James Hopkin
fonte
2
-1. Se o seu raciocínio estiver correto, o mesmo raciocínio implicaria que declarações avançadas de tipos de classe não pudessem ser usadas para criar ponteiros para esses tipos - mas eles podem.
j_random_hacker
6
+1. O raciocínio está correto. O caso específico são plataformas em que sizeof (char *)> sizeof (int *). Ambos podem ser tipos subjacentes para uma enumeração, dependendo do intervalo. As classes não têm tipos subjacentes, portanto a analogia é falsa.
MSalters
3
@MSalters: Exemplo: "struct S {int x;};" Agora, sizeof (S *) deve ser igual ao tamanho de qualquer outro struct ponteiro-to-, desde C ++ permite tal ponteiro para ser declarado e utilizado antes da definição de S ...
j_random_hacker
1
@MSalters: ... Em uma plataforma em que sizeof (char *)> sizeof (int *), o uso de um ponteiro "em tamanho normal" para essa estrutura específica pode ser ineficiente, mas simplifica drasticamente a codificação - e exatamente a mesma algo poderia e deveria ser feito para tipos de enumeração.
j_random_hacker
4
ponteiros para dados e ponteiros para funções podem ter tamanhos diferentes, mas tenho quase certeza de que os ponteiros de dados precisam fazer uma viagem de ida e volta (transmitir para outro tipo de ponteiro de dados, depois voltar ao original, ainda precisa funcionar), o que implica que todos ponteiros de dados são do mesmo tamanho.
Ben Voigt
7

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.

Alexey Feldgendler
fonte
5

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

enum X;

(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:

enum X : int;

É 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.2no rascunho de trabalho atual.

Johannes Schaub - litb
fonte
4

Eu faria assim:

[no cabeçalho público]

typedef unsigned long E;

void Foo(E e);

[no cabeçalho interno]

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

Ao adicionar FORCE_32BIT, garantimos que o Econtent seja compilado por muito tempo, para que seja intercambiável com E.

Laurie Cheers
fonte
1
Obviamente, isso significa que (A) os tipos de E e Econtent diferem e (B) nos sistemas LP64, tamanhoof (E) = 2 * tamanhoof (EContent). Correção trivial: ULONG_MAX, mais fácil de ler também.
MSalters
2

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:

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

Então, no seu arquivo de implementação (cpp), você declara uma classe que será a representação dos internos.

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

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

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:

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

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.

Vincent Robert
fonte
3
struct AImpl; struct A {private: AImpl * pImpl; };
2

Você pode agrupar a enum em uma estrutura, adicionando alguns construtores e digitar conversões e, em vez disso, declarar a estrutura.

#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; \
}

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

Leszek Swirski
fonte
1

Parece que não pode ser declarado para a frente no GCC!

Discussão interessante aqui

prakash
fonte
1

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:

O tipo subjacente de uma enumeração é um tipo integral que pode representar todos os valores do enumerador definidos na enumeração. É definido pela implementação qual tipo integral é usado como o tipo subjacente para uma enumeração, exceto que o tipo subjacente não deve ser maior que int, a menos que o valor de um enumerador não possa caber em um int ou não assinado. Se a lista de enumeradores estiver vazia, o tipo subjacente é como se a enumeração tivesse um único enumerador com o valor 0. O valor de sizeof () aplicado a um tipo de enumeração, um objeto do tipo de enumeração ou um enumerador, é o valor de sizeof () aplicado ao tipo subjacente.

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:

Uma classe que foi declarada mas não definida, ou uma matriz de tamanho desconhecido ou de tipo de elemento incompleto, é um tipo de objeto definido de maneira incompleta.

Um tipo de classe (como "classe X") pode estar incompleto em um ponto da unidade de tradução e ser concluído posteriormente; o tipo "classe X" é o mesmo tipo nos dois pontos. O tipo declarado de um objeto de matriz pode ser uma matriz do tipo de classe incompleta e, portanto, incompleta; se o tipo de classe for concluído posteriormente na unidade de conversão, o tipo de matriz se tornará completo; o tipo de matriz nesses dois pontos é do mesmo tipo. O tipo declarado de um objeto de matriz pode ser uma matriz de tamanho desconhecido e, portanto, incompleto em um ponto da unidade de conversão e concluído posteriormente; os tipos de matriz nesses dois pontos ("matriz de limite desconhecido de T" e "matriz de N T") são tipos diferentes. O tipo de um ponteiro para matriz de tamanho desconhecido ou de um tipo definido por uma declaração typedef para ser uma matriz de tamanho desconhecido,

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.

Dan Olson
fonte
observe que ele explicitamente não permite "enum foo"; em 7.1.5.3/1 (mas como em tudo, desde que o compilador adverte, pode ainda compilar esse código, é claro)
Johannes Schaub - litb
Obrigado por apontar, esse é um parágrafo realmente esotérico e pode levar uma semana para analisá-lo. Mas é bom saber que está lá.
2111 Dan Danson
não se preocupe. alguns parágrafos padrão são realmente estranhos :) bem, um especificador de tipo elaborado é algo em que você especifica um tipo, mas também especifica algo mais para torná-lo inequívoco. por exemplo, "struct X" em vez de "X" ou "enum Y" em vez de apenas "Y". você precisa afirmar que algo é realmente um tipo.
Johannes Schaub - litb 27/03/09
então você pode usá-lo assim: "class X * foo;" se X não foi declarado para a frente ainda. ou "typename X :: foo" em um modelo para desambiguação. ou "obj de link de classe"; se houver uma função "link" no mesmo escopo que ocultaria a classe com o mesmo nome.
Johannes Schaub - litb 27/03/09
no 3.4.4, diz que eles são usados ​​se algum nome que não seja do tipo ocultar um nome de tipo. é onde eles são usados ​​com mais freqüência, além de declarar como "classe X"; (aqui é a única constituição de uma declaração). ele fala sobre eles em não modelos aqui. no entanto, 14.6 / 3 lista um uso deles em modelos.
Johannes Schaub - litb 27/03/09
1

Para VC, aqui está o teste sobre a declaração direta e a especificação do tipo subjacente:

  1. o seguinte código é compilado ok.
    typedef int myint;
    enumeração T;
    void foo (T * tp)
    {
        * tp = (T) 0x12345678;
    }
    enumeração T: char
    {
        UMA
    };

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'

  1. O VC (Microsoft (R) C / C ++ Optimizer Compiler versão 15.00.30729.01 para 80x86) parece com erros no caso acima:

    • ao ver enum T; O VC assume que o tipo de enumeração T usa 4 bytes padrão int como tipo subjacente; portanto, o código de montagem gerado é:
    ? foo @@ YAXPAW4T @@@ Z PROC; foo
    ; Arquivo e: \ work \ c_cpp \ cpp_snippet.cpp
    ; Linha 13
        push ebp
        mov ebp, esp
    ; Linha 14
        mov eax, DWORD PTR _tp $ [ebp]
        mov DWORD PTR [eax], 305419896; 12345678H
    ; Linha 15
        pop ebp
        ret 0
    ? foo @@ YAXPAW4T @@@ Z ENDP; foo

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:

    int main (int argc, char * argv)
    {
        União {
            char ca [4];
            Tt;
        }uma;
        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]) ;
        retornar 0;
    }

o resultado é: 0x78, 0x56, 0x34, 0x12

  • após remover a declaração direta da enumeração T e mover a definição da função foo após a definição da enumeração T: o resultado é OK:

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.

zhaorufei
fonte
1

Nos meus projetos, adotei a técnica de enumeração vinculada a namespace para lidar com enums de componentes herdados e de terceiros. Aqui está um exemplo:

forward.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;
}

Observe que o foo.hcabeçalho não precisa saber nada sobre legacy::evil. Somente os arquivos que usam o tipo herdado legacy::evil(aqui: main.cc) precisam ser incluídos enum.h.

mavam
fonte
0

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):

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

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:

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: 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.

paercebal
fonte
-1

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.

Carl Seleborg
fonte
-1

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.

xtofl
fonte
1
Não, o código subsequente não precisa conhecer os valores para que isso seja útil - em particular, se o código subsequente for apenas um protótipo de função que aceita ou retorna parâmetros de enumeração, o tamanho do tipo não é importante. O uso da declaração direta aqui pode remover as dependências de compilação, acelerando a compilação.
j_random_hacker
Você está certo. A intenção não é obedecer aos valores, mas ao tipo. Resolvido com os tipos 0x Enum.
xtofl 26/03/09