J'essaie de faire quelque chose comme ceci:
enum E;
void Foo(E e);
enum E {A, B, C};
que le compilateur rejette. J'ai jeté un rapide coup d'œil sur Google et le consensus semble être "vous ne pouvez pas le faire", mais je ne comprends pas pourquoi. Quelqu'un peut-il expliquer?
Clarification 2: je fais cela car j'ai des méthodes privées dans une classe qui prennent ladite énumération, et je ne veux pas que les valeurs de l'énumération soient exposées - donc, par exemple, je ne veux pas que quiconque sache que E est défini comme
enum E {
FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}
car le projet X n'est pas quelque chose que je veux que mes utilisateurs connaissent.
Donc, je voulais transmettre déclarer l'énumération afin de pouvoir mettre les méthodes privées dans le fichier d'en-tête, déclarer l'énumération en interne dans le cpp et distribuer le fichier et l'en-tête de la bibliothèque construite aux personnes.
Quant au compilateur - c'est GCC.
Réponses:
La raison pour laquelle l'énum ne peut pas être déclarée en avant est que sans connaître les valeurs, le compilateur ne peut pas connaître le stockage requis pour la variable enum. Les compilateurs C ++ sont autorisés à spécifier l'espace de stockage réel en fonction de la taille nécessaire pour contenir toutes les valeurs spécifiées. Si tout ce qui est visible est la déclaration directe, l'unité de traduction ne peut pas savoir quelle taille de stockage aura été choisie - il pourrait s'agir d'un caractère ou d'un int, ou autre chose.
De la section 7.2.5 de la norme ISO C ++:
Étant donné que l' appelant à la fonction doit connaître la taille des paramètres pour configurer correctement la pile d'appels, le nombre d'énumérations dans une liste d'énumération doit être connu avant le prototype de la fonction.
Mise à jour: en C ++ 0X, une syntaxe de déclaration des types d'énumération a été proposée et acceptée. Vous pouvez voir la proposition à http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
la source
struct S; void foo(S s);
(notez quefoo
c'est seulement déclaré, non défini), alors il n'y a aucune raison pour que nous ne puissions pas faireenum E; void foo(E e);
aussi bien. Dans les deux cas, la taille n'est pas nécessaire.La déclaration anticipée des énumérations est possible depuis C ++ 11. Auparavant, la raison pour laquelle les types d'énumération ne pouvaient pas être déclarés avant était parce que la taille de l'énumération dépend de son contenu. Tant que la taille de l'énumération est spécifiée par l'application, elle peut être déclarée en avant:
la source
J'ajoute une réponse à jour ici, compte tenu des développements récents.
Vous pouvez déclarer une énumération en aval dans C ++ 11, tant que vous déclarez son type de stockage en même temps. La syntaxe ressemble à ceci:
En fait, si la fonction ne fait jamais référence aux valeurs de l'énumération, vous n'avez pas du tout besoin de la déclaration complète à ce stade.
Ceci est pris en charge par G ++ 4.6 et versions ultérieures (
-std=c++0x
ou-std=c++11
dans les versions plus récentes). Visual C ++ 2013 prend en charge cela; dans les versions antérieures, il a une sorte de support non standard que je n'ai pas encore compris - j'ai trouvé une suggestion qu'une simple déclaration directe est légale, mais YMMV.la source
enum class
tant qu'extension C ++ (avant C ++ 11 différentenum class
), du moins si je me souviens bien. Le compilateur vous a permis de spécifier le type sous-jacent d'une énumération, mais ne prend pas en chargeenum class
ou les énumérations déclarées vers l'avant, et vous a averti que la qualification d'un énumérateur avec la portée de l'énumération était une extension non standard. Je me souviens que cela fonctionnait à peu près de la même manière que la spécification du type sous-jacent en C ++ 11, sauf plus ennuyeux car vous deviez supprimer l'avertissement.La déclaration directe de choses en C ++ est très utile car elle accélère considérablement le temps de compilation . Vous pouvez déclarer avant plusieurs choses en C ++ , y compris:
struct
,class
,function
, etc ...Mais pouvez-vous transmettre une déclaration
enum
en C ++?Non tu ne peux pas.
Mais pourquoi ne pas le permettre? S'il était autorisé, vous pourriez définir votre
enum
type dans votre fichier d'en-tête et vosenum
valeurs dans votre fichier source. On dirait que cela devrait être autorisé non?Faux.
En C ++, il n'y a pas de type par défaut
enum
comme en C # (int). En C ++ votreenum
type sera déterminé par le compilateur comme n'importe quel type qui s'adaptera à la plage de valeurs que vous avez pour votreenum
.Qu'est-ce que ça veut dire?
Cela signifie que le
enum
type sous-jacent de votre ne peut pas être entièrement déterminé tant que vous n'avez pas toutes les valeurs duenum
défini. Quel mans vous ne pouvez pas séparer la déclaration et la définition de votreenum
. Et par conséquent, vous ne pouvez pas transmettre de déclarationenum
en C ++.La norme ISO C ++ S7.2.5:
Vous pouvez déterminer la taille d'un type énuméré en C ++ à l'aide de l'
sizeof
opérateur. La taille du type énuméré est la taille de son type sous-jacent. De cette façon, vous pouvez deviner quel type votre compilateur utilise pour votreenum
.Et si vous spécifiez le type de votre
enum
explicitement comme ceci:Pouvez-vous ensuite transmettre votre déclaration
enum
?Mais pourquoi pas?
La spécification du type d'un
enum
ne fait pas réellement partie de la norme C ++ actuelle. Il s'agit d'une extension VC ++. Il fera cependant partie de C ++ 0x.La source
la source
[Ma réponse est fausse, mais je l'ai laissée ici parce que les commentaires sont utiles].
La déclaration en avant des énumérations n'est pas standard, car les pointeurs vers différents types d'énumérations ne sont pas garantis de la même taille. Le compilateur peut avoir besoin de voir la définition pour savoir quelles pointeurs de taille peuvent être utilisés avec ce type.
En pratique, au moins sur tous les compilateurs populaires, les pointeurs vers les énumérations ont une taille cohérente. La déclaration en avant des énumérations est fournie comme une extension de langage par Visual C ++, par exemple.
la source
Il n’existe en effet pas de déclaration d’énumération. Comme la définition d'une énumération ne contient aucun code pouvant dépendre d'un autre code utilisant l'énumération, il n'est généralement pas difficile de définir complètement l'énumération lorsque vous la déclarez pour la première fois.
Si la seule utilisation de votre énumération est par des fonctions de membre privé, vous pouvez implémenter l'encapsulation en ayant l'énumération elle-même en tant que membre privé de cette classe. L'énumération doit encore être entièrement définie au point de déclaration, c'est-à-dire dans la définition de classe. Cependant, ce n'est pas un problème plus important que d'y déclarer des fonctions de membre privé, et ce n'est pas une exposition pire des implémentations internes que cela.
Si vous avez besoin d'un degré de dissimulation plus profond pour les détails de votre implémentation, vous pouvez le diviser en une interface abstraite, composée uniquement de fonctions virtuelles pures, et d'une classe concrète complètement cachée implémentant (héritant) l'interface. La création d'instances de classe peut être gérée par une fabrique ou une fonction membre statique de l'interface. De cette façon, même le vrai nom de classe, sans parler de ses fonctions privées, ne sera pas exposé.
la source
Il suffit de noter que la raison en fait est que la taille de l'énumération n'est pas encore connue après la déclaration préalable. Eh bien, vous utilisez la déclaration directe d'une structure pour pouvoir passer un pointeur ou faire référence à un objet à partir d'un endroit auquel il est également fait référence dans la définition de la structure déclarée directe.
La déclaration directe d'une énumération ne serait pas trop utile, car on souhaiterait pouvoir contourner la sous-valeur de l'énumération. Vous ne pouviez même pas avoir de pointeur dessus, car on m'a récemment dit que certaines plateformes utilisaient des pointeurs de taille différente pour char que pour int ou long. Tout dépend donc du contenu de l'énumération.
La norme C ++ actuelle interdit explicitement de faire quelque chose comme
(en
7.1.5.3/1
). Mais la prochaine norme C ++ en raison de l'année prochaine permet ce qui suit, ce qui m'a convaincu que le problème est en fait lié au type sous-jacent:Il est connu comme une déclaration d'énumération «opaque». Vous pouvez même utiliser X par valeur dans le code suivant. Et ses énumérateurs peuvent être définis ultérieurement dans une redéclaration ultérieure de l'énumération. Voir
7.2
dans le projet de travail actuel.la source
Je le ferais de cette façon:
[dans l'en-tête public]
[dans l'en-tête interne]
En ajoutant FORCE_32BIT, nous nous assurons qu'Econtent se compile sur une longue, il est donc interchangeable avec E.
la source
Si vous ne voulez vraiment pas que votre énumération apparaisse dans votre fichier d'en-tête ET assurez-vous qu'elle n'est utilisée que par des méthodes privées, alors une solution peut être d'aller avec le principe de pimpl.
C'est une technique qui garantit de cacher les internes de classe dans les en-têtes en déclarant simplement:
Ensuite, dans votre fichier d'implémentation (cpp), vous déclarez une classe qui sera la représentation des internes.
Vous devez créer dynamiquement l'implémentation dans le constructeur de classe et la supprimer dans le destructeur et lors de l'implémentation de la méthode publique, vous devez utiliser:
Il y a des avantages à utiliser pimpl, l'un est qu'il dissocie votre en-tête de classe de son implémentation, pas besoin de recompiler d'autres classes lors du changement d'implémentation d'une classe. Un autre est que cela accélère votre temps de compilation car vos en-têtes sont si simples.
Mais c'est difficile à utiliser, vous devriez donc vraiment vous demander si le simple fait de déclarer votre énumération privée dans l'en-tête est un problème.
la source
Vous pouvez encapsuler l'énumération dans une structure, en ajoutant certains constructeurs et conversions de type, et déclarer la structure à la place.
Cela semble fonctionner: http://ideone.com/TYtP2
la source
Semble qu'il ne peut pas être déclaré à l'avance dans GCC!
Discussion intéressante ici
la source
Il y a une certaine dissidence depuis que cela a été heurté (en quelque sorte), alors voici quelques bits pertinents de la norme. La recherche montre que la norme ne définit pas vraiment la déclaration directe, ni n'indique explicitement que les énumérations peuvent ou ne peuvent pas être déclarées avant.
Tout d'abord, à partir de dcl.enum, section 7.2:
Ainsi, le type sous-jacent d'une énumération est défini par l'implémentation, avec une restriction mineure.
Ensuite, nous passons à la section sur les "types incomplets" (3.9), qui est à peu près aussi proche que possible de toute norme sur les déclarations à terme:
Donc, là, la norme énonçait à peu près les types qui peuvent être déclarés. Enum n'était pas là, donc les auteurs de compilateurs considèrent généralement la déclaration avant comme interdite par la norme en raison de la taille variable de son type sous-jacent.
Cela a également du sens. Les énumérations sont généralement référencées dans des situations par valeur, et le compilateur aurait en effet besoin de connaître la taille de stockage dans ces situations. Étant donné que la taille de stockage est définie par l'implémentation, de nombreux compilateurs peuvent simplement choisir d'utiliser des valeurs de 32 bits pour le type sous-jacent de chaque énumération, auquel cas il devient possible de les déclarer en avant. Une expérience intéressante pourrait être d'essayer de déclarer une énumération dans Visual Studio, puis de la forcer à utiliser un type sous-jacent supérieur à sizeof (int) comme expliqué ci-dessus pour voir ce qui se passe.
la source
Pour VC, voici le test sur la déclaration directe et la spécification du type sous-jacent:
Mais j'ai reçu l'avertissement pour / W4 (/ W3 n'encourra pas cet avertissement)
avertissement C4480: extension non standard utilisée: spécification du type sous-jacent pour l'énumération 'T'
VC (Microsoft (R) 32-bit C / C ++ Optimizing Compiler Version 15.00.30729.01 pour 80x86) semble bogué dans le cas ci-dessus:
Le code assembleur ci-dessus est extrait directement de /Fatest.asm, pas ma supposition personnelle. Voyez-vous le mov DWORD PTR [eax], 305419896; Ligne 12345678H?
l'extrait de code suivant le prouve:
le résultat est: 0x78, 0x56, 0x34, 0x12
l'instruction clé ci-dessus devient:
mov BYTE PTR [eax], 120; 00000078H
le résultat final est: 0x78, 0x1, 0x1, 0x1
Notez que la valeur n'est pas écrasée
Par conséquent, l'utilisation de la déclaration en avant d'énumération dans VC est considérée comme nuisible.
BTW, pour ne pas surprendre, la syntaxe de déclaration du type sous-jacent est la même qu'en C #. Dans la pratique, j'ai trouvé qu'il valait la peine d'économiser 3 octets en spécifiant le type sous-jacent comme char lorsque vous parlez au système intégré, qui est limité en mémoire.
la source
Dans mes projets, j'ai adopté la technique d' énumération liée à l' espace de noms pour gérer les
enum
s des composants hérités et tiers. Voici un exemple:forward.h:
enum.h:
foo.h:
foo.cc:
main.cc:
Notez que l'en-
foo.h
tête n'a rien à savoirlegacy::evil
. Seuls les fichiers qui utilisent le type héritélegacy::evil
(ici: main.cc) doivent être inclusenum.h
.la source
Ma solution à votre problème serait de:
1 - utilisez int au lieu d'enums: Déclarez vos entrées dans un espace de noms anonyme dans votre fichier CPP (pas dans l'en-tête):
Comme vos méthodes sont privées, personne ne gâchera les données. Vous pouvez même aller plus loin pour tester si quelqu'un vous envoie des données invalides:
2: créer une classe complète avec des instanciations const limitées, comme fait en Java. Déclarez la classe, puis définissez-la dans le fichier CPP, et instanciez uniquement les valeurs de type énumération. J'ai fait quelque chose comme ça en C ++, et le résultat n'était pas aussi satisfaisant que souhaité, car il avait besoin de code pour simuler une énumération (construction de copie, opérateur =, etc.).
3: Comme proposé précédemment, utilisez l'énumération déclarée privée. Malgré le fait qu'un utilisateur verra sa définition complète, il ne pourra pas l'utiliser, ni utiliser les méthodes privées. Ainsi, vous pourrez généralement modifier l'énumération et le contenu des méthodes existantes sans avoir besoin de recompiler le code à l'aide de votre classe.
Je suppose que ce serait la solution 3 ou 1.
la source
Étant donné que l'énumération peut être une taille intégrale de taille variable (le compilateur décide de la taille d'une énumération donnée), le pointeur vers l'énumération peut également avoir une taille variable, car il s'agit d'un type intégral (les caractères ont des pointeurs d'une taille différente sur certaines plates-formes). par exemple).
Ainsi, le compilateur ne peut même pas vous laisser déclarer en avant l'énumération et utiliser un pointeur vers celle-ci, car même là, il a besoin de la taille de l'énumération.
la source
Vous définissez une énumération pour restreindre les valeurs possibles des éléments du type à un ensemble limité. Cette restriction doit être appliquée au moment de la compilation.
Lorsque vous déclarez par la suite que vous utiliserez un «ensemble limité» plus tard, cela n'ajoute aucune valeur: le code suivant doit connaître les valeurs possibles pour en bénéficier.
Bien que le compilateur soit préoccupé par la taille du type énuméré, l' intention de l'énumération est perdue lorsque vous la déclarez.
la source