Déclarations avant de struct sans nom

Question Bounty: Donc, ces deux Foo ne sont pas la même chose. Bien. La deuxième forme est donnée dans une bibliothèque. Comment puis-je le déclarer à l’avance, étant donné que je ne peux pas le changer?


J’ai toujours pensé que C et C ++ permettaient des déclarations répétées à condition qu’il n’y ait pas de définitions répétées. Puis je suis tombé sur ce problème en essayant d’écrire du code C ++ qui étend une bibliothèque C.

 struct Foo; typedef struct {} Foo; 

Cela donne l’erreur suivante:

‘struct Foo’ a une déclaration précédente en tant que ‘struct Foo’

Je veux faire une déclaration préalable, sacrément! Qu’est-ce qui ne va pas ici?

La structure anonyme de typedef-ing est une pratique antérieure à C ++ 03 et principalement orientée pour conserver la compatibilité avec les compilateurs antérieurs à C99.

Étant donné que nous sums en 2011 et que C ++ et C sont modifiés, je me demande pourquoi il n’ya pas de version plus récente de cette bibliothèque!

Si ce n’est plus en développement, vous ne pouvez pas “partir”, mais simplement “survivre” et le changer est la façon de le faire. Si vous êtes toujours en déploiement, soumettez le problème à l’équipe de développement.

Si vous avez besoin d’une solution de contournement, considérez que struct peut hériter. Donc, écrivez une déclaration anticipée comme

 struct MyFoo; 

et le définir comme

 #include "old_library.h" struct MyFoo: public Foo {}; 

Et dans tout votre code, oubliez Foo et utilisez toujours MyFoo .

Vous déclarez deux entités différentes portant le même nom. Le premier, struct Foo , est une structure nommée Foo . Le second est un alias pour une structure anonyme .

Si vous le faites à la place:

 struct Foo; struct Foo {}; 

Cela fonctionne, car vous déclarez une structure nommée Foo dans les deux situations.

Vous ne pouvez pas transmettre de déclarations anonymes. Vous avez deux choix: inclure la définition entière ou changer l’en-tête et nommer la structure.

Vous n’avez pas besoin de typeref structs en C ++:

 struct Foo; // Forward declaration struct Foo { }; // Definition 

Si vous voulez l’appeler simplement Foo au lieu de struct Foo en C, vous avez besoin du typedef, qui peut également être fait de différentes manières:

 struct Foo; /* Forward declaration */ struct Foo /* The name is needed here */ { }; /* Definition */ typedef struct Foo Foo; /* typedef */ 

ou

 struct Foo; /* Forward declaration */ typedef struct Foo /* The name is needed here */ { } Foo; /* Definition and typedef combined */ 

Vous pouvez bien sûr utiliser le formulaire struct Foo en C et C ++.

Dans une situation similaire, j’ai un ancien en-tête C avec quelque chose comme

 == old_library.h == typedef struct { int data; } Foo; == /old_library.h == 

Je l’utilise dans une classe C ++ de mon propre chef, en tant que paramètre pour une méthode privée:

 class C { void run(Foo *ds); ... } 

Pour éviter #include “old_library.h” de C.hpp, j’utilise la déclaration suivante:

 class C { struct Foo; void run(Foo *ds); ... } 

et dans C.cpp j’ai les déclarations suivantes:

 extern "C" { #include "old_library.h" } struct C::Foo : public ::Foo {}; 

De cette façon, j’utilise C :: Foo au lieu de Foo de manière transparente, et je n’ai pas besoin de MyFoo!

Votre déclaration anticipée déclare qu’il y aura une struct appelée Foo .

Votre deuxième déclaration est d’un typedef appelé Foo . Ce ne sont pas les mêmes choses.

Pour une déclaration en aval d’un typedef, vous devez vous référer à la chose qui est en train d’être tapée, donc:

 struct foo; typedef foo bar; class foo{}; 

Comme vous voulez transférer une structure anonyme, vous ne pouvez ni lui donner un nom dans la déclaration forward de l’entité d’origine, ni vous y référer lorsque vous la tapez. La syntaxe “logique” serait:

 struct ; typedef bar; class {}; 

Mais comme ce n’est évidemment pas possible, vous ne pouvez pas transmettre de déclarations anonymes.

Pour aller standard, regardons 9.1-2:

Une déclaration consistant uniquement en un identifiant de clé de classe; est soit une redéclaration du nom dans la scope en cours, soit une déclaration directe de l’identifiant en tant que nom de classe. Il introduit le nom de la classe dans la scope actuelle.

Pas d’identifiant, pas de déclaration à terme.

En résumé, évitez les structures anonymes, à moins qu’elles ne vous procurent un avantage réel.

IMHO, changez simplement votre typedef pour,

 typedef struct Foo {} Foo; ^^^^^ 

Il n’y a pas de mal et il sera toujours compatible à la fois en C & C ++. Vous pouvez maintenant le déclarer.

[Note: Si vous insistez toujours pour ne pas toucher au typedef du tout, alors voici le sale tour .

 struct Foo; #define struct struct Foo #include"Foo.h" // contains typedef struct {} Foo; #undef struct 

Cela fonctionnera seulement si Foo.h ne contient que 1 déclaration de struct . Je ne le recommande pas.]

Votre compilateur spécifique peut faire la différence ici.

À l’aide de MinGW GCC 3.4.5, les deux déclarations sont compilées sans erreur ni avertissement (avec -Wall )

 struct Foo; typedef struct {} Foo; 

et

 struct Foo; typedef struct Foo {} Foo; 

Est-il possible qu’une déclaration préalable existe déjà dans la bibliothèque? Par exemple, pour permettre un pointeur circulaire:

 struct Foo; typedef struct Foo { struct Foo *fooPtr; } Foo; 

Si cela existe déjà dans les en-têtes de la bibliothèque, cela provoquerait l’erreur que vous décrivez.

Pourquoi n’évitez-vous pas simplement la déclaration anticipée?

Si la deuxième définition se trouvait dans un fichier d’en-tête, vous pouvez d’abord inclure le fichier d’en-tête dans votre fichier d’en-tête C ++. Pour que C ++ le considère comme un en-tête C, inclure #include avec extern "C" { et } doit suffire dans la plupart des cas.

Je pense avoir récemment rencontré le même problème que l’affiche originale et l’avoir résolu comme suit:

J’écris un wrapper autour d’une API tierce fournie définie comme suit:

Foo.h:

 typedef struct _foo { int i; } Foo; Foo* MakeFoo(); int UseFoo(Foo* foo); 

Mon wrapper doit avoir Foo en tant que membre, mais je ne veux pas exposer Foo à tous les consommateurs de mon wrapper.

UberFoo.h:

 #pragma once struct _foo; // forward declare the actual type _foo, not the typedef Foo class UberFoo { public: UberFoo(); int GetAnswer(); private: _foo* f; }; 

UberFoo.cpp:

 #include "UberFoo.h" #include "Foo.h" UberFoo::UberFoo() { this->f = MakeFoo(); } int UberFoo::GetAnswer() { return UseFoo(f); } 

Maintenant, les consommateurs de ma classe peuvent l’instancier sans avoir access à la définition réelle de _foo / Foo. Cela fonctionnerait aussi si je devais passer des pointeurs à _foo en tant que parameters ou les renvoyer à partir de fonctions et avoir un membre _foo.

main.cpp:

 #include  #include "UberFoo.h" int main(int argc, char* argv[]) { UberFoo u; printf( "The Answer = %d\n", u.GetAnswer()); return 0; } 

L’astuce consistait à transmettre le type de structure actuel, et non le nom de typedef. Notez qu’il est essentiel que la structure ne soit pas anonyme, comme cela est courant dans l’ancien code C:

 typedef struct // the actual struct type underlying the typedef is anonymous here { int i; } ThisWontWork; 

J’espère que cela t’aides!

Vous ne pouvez pas le déclarer depuis son nom. C’est une structure sans nom, pour laquelle Foo est un typedef.

Vous essayez de saisir un nom précédemment utilisé. La déclaration est parfaitement valide, seul vous devez utiliser un nom différent.

 struct Foo; // Forward declaration of struct Foo typedef struct {} anotherFoo; // Another structure typedefed struct Foo { // Variables }; // Definition of the forward declared Foo. 

Notez que le typedef ne peut pas être utilisé sous le même nom.