Pourquoi C ++ interdit-il les structures anonymes?

92

Certains compilateurs C ++ autorisent les unions et les structures anonymes comme extension du C ++ standard. C'est un peu de sucre syntaxique qui est parfois très utile.

Quelle est la raison pour laquelle cela ne fait pas partie de la norme? Y a-t-il un barrage technique? Un philosophique? Ou tout simplement pas assez de besoin pour le justifier?

Voici un échantillon de ce dont je parle:

struct vector3 {
  union {
    struct {
      float x;
      float y;
      float z;
    };
    float v[3];
  };
};

Mon compilateur acceptera cela, mais il avertit que "struct / union sans nom" est une extension non standard de C ++ .

Adrian McCarthy
la source
3
Il y a clairement une certaine confusion sur ce que vous voulez dire. Pourriez-vous s'il vous plaît fournir un exemple de code qui se compile uniquement en raison d'une extension de compilateur?
Rob Kennedy
73
Notez qu'il existe deux concepts, qui semblent similaires, mais sont très différents: les structures sans nom et les structures anonymes . Le premier est celui-ci, que C ++ prend en charge: struct { int i; } a; a.i = 0;(le type n'a pas de nom). Le second est celui-ci, que C ++ ne prend pas en charge: struct { int i; }; i = 0;(le type n'a pas de nom, et il s'échappe dans la portée environnante). C ++, cependant, prend en charge les unions non nommées et anonymes .
Johannes Schaub - litb
Cela ressemble à la bibliothèque vectorielle VMMLib plutôt intéressante. Je crois que le problème est que le syndicat contient une structure sans nom, mais je ne suis pas sûr.
greyfade
1
FWIW C'est "anonyme", pas "sans nom", et les syndicats sont soutenus comme le dit litb. stackoverflow.com/q/14248044/560648
Courses de légèreté en orbite
1
@AdrianMcCarthy: C'est très bien (FSVO "bien"; le compilateur embêtant étant cryptique), mais précisément "sans nom" est un concept standard sans rapport.
Courses de légèreté en orbite le

Réponses:

50

Comme d'autres l'ont souligné, les unions anonymes sont autorisées dans le C ++ standard, mais les structures anonymes ne le sont pas.

La raison en est que C prend en charge les unions anonymes mais pas les structures anonymes *, donc C ++ prend en charge la première pour la compatibilité mais pas la seconde car elle n'est pas nécessaire pour la compatibilité.

De plus, les structures anonymes en C ++ sont peu utiles. L'utilisation que vous démontrez, d'avoir une struct contenant trois flotteurs qui peuvent être désignés soit par .v[i], ou .x, .yet .z, je crois que les résultats dans un comportement non défini en C ++. C ++ ne vous permet pas d'écrire sur un membre d'une union, par exemple .v[1], puis de lire à partir d'un autre membre, par exemple .y. Bien que le code qui le fasse ne soit pas rare, il n'est pas vraiment bien défini.

Les fonctionnalités de C ++ pour les types définis par l'utilisateur fournissent des solutions alternatives. Par exemple:

struct vector3 {
  float v[3];
  float &operator[] (int i) { return v[i]; }
  float &x() { return v[0]; }
  float &y() { return v[1]; }
  float &z() { return v[2]; }
};

* C11 ajoute apparemment des structures anonymes, donc une future révision de C ++ pourrait les ajouter.

bames53
la source
2
+1: Mon exemple repose sur un comportement non défini en C ++ - quelque chose dont je n'étais pas au courant lorsque j'ai écrit la question.
Adrian McCarthy
2
"C ++ ne vous permet pas d'écrire à un membre d'un syndicat [...] puis de lire à partir d'un autre membre" - sauf si lesdits membres sont des objets de mise en page standard et partagent une séquence initiale commune de membres, et vous ' réécrire / lire leurs membres dans ladite séquence initiale commune. Cela est autorisé (c'est-à-dire défini).
underscore_d
5
@underscore_d: Oui, si les types sont une disposition standard avec une séquence initiale commune. Cependant, une structure ne peut jamais créer d'alias avec un tableau de cette manière, car les règles de "séquence initiale commune" de C ++ indiquent qu'une séquence initiale commune ne peut être qu'entre les structures . Les tableaux ne sont pas mentionnés, ils ne peuvent donc pas utiliser d'alias comme celui-ci.
Nicol Bolas
@NicolBolas Oh, haha ​​- croyez-moi - j'ai souhaité à plusieurs reprises que les tableaux et autres primitifs soient inclus dans cette allocation! Mais je n'ai pas beaucoup réfléchi aux limites pratiques possibles à ce sujet, donc ce n'est probablement pas aussi simple qu'il n'y paraît actuellement. Mon commentaire était plus général mais aurait pu risquer d'impliquer par omission que je pensais que les tableaux étaient inclus, alors merci de l'ajouter.
underscore_d
"La raison en est que C prend en charge les unions anonymes mais pas les structures anonymes" - Non. Votre note de bas de page précise que vous parliez de C99 ou plus tôt ici. Le terme «union anonyme» n'apparaît nulle part dans la norme C99. GCC affirme dans un diagnostic (avec -std = c99 -pedantic options) que "ISO C99 ne supporte pas les structures / unions sans nom". La norme ne mentionne rien sur les membres non nommés autres que les champs de bits sans nom. Je ne suis pas tout à fait sûr que les déclarations de structure soient des déclarations, mais si elles le sont, les unions anonymes sont une violation de contrainte selon 6.7p2, au mieux non définie.
21

Je dirai que vous pouvez nettoyer votre vector3déclaration en utilisant simplement ununion

union vector3 {
  struct { float x, y, z; } ;
  float v[3] ;
} ;

Bien sûr, les structures anonymes étaient une extension MSVC . Mais ISO C11 le permet maintenant, et gcc le permet , tout comme le compilateur llvm d'Apple.

Pourquoi en C11 et non en C ++ 11? Je ne suis pas sûr, mais pratiquement, la plupart des compilateurs C ++ (gcc ++, MSVC ++ et le compilateur C ++ d'Apple) les prennent en charge.

bobobobo
la source
1
+1 pour des informations mises à jour. La raison pour laquelle j'avais une structure externe était que le "vrai code" avait également des méthodes.
Adrian McCarthy
Les seules choses que vous ne pouvez pas faire avec une union sont d' avoir des membres de données statiques ou d'utiliser l'héritage .
bobobobo
2
Merci. Jamais une nouvelle union ne pourrait être utilisée comme une structure ou une classe.
Adrian McCarthy
Je sais que Sun studio ne prenait pas en charge les structures anonymes avant C ++ 11 par défaut. Si vous écrivez du code multiplateforme et que les compilateurs ne sont pas mis à niveau vers C + 11, n'utilisez pas de struct anonyme.
irsis
6

Pas sûr de ce que vous voulez dire. Section 9.5 de la spécification C ++, clause 2:

Une union de la forme

union { member-specification } ;

s'appelle une union anonyme; il définit un objet sans nom de type sans nom.

Vous pouvez également faire des choses comme celle-ci:

void foo()
{
  typedef
  struct { // unnamed, is that what you mean by anonymous?
    int a;
    char b;
  } MyStructType; // this is more of a "C" style, but valid C++ nonetheless

  struct { // an anonymous struct, not even typedef'd
    double x;
    double y;
  } point = { 1.0, 3.4 };
}

Pas toujours très utile ... bien que parfois utile dans les définitions de macro désagréables.

Dan
la source
11
-1 parce qu'il dit qu'il définit une structure anonyme. Voir les commentaires ci-dessus sur la question - vous définissez une structure sans nom, pas une structure anonyme.
Johannes Schaub - litb
1

Les syndicats peuvent être anonymes; voir la norme, 9.5 paragraphe 2.

À quoi pensez-vous qu'une structure ou une classe anonyme remplit? Avant de spéculer pourquoi quelque chose n'est pas dans la norme, j'aimerais avoir une idée de pourquoi cela devrait être, et je ne vois pas d'utilisation pour une structure anonyme.

David Thornley
la source
1

Sur la base de la modification, des commentaires et de cet article MSDN: Structures anonymes , je vais risquer une supposition - cela correspond mal au concept d'encapsulation. Je ne m'attendrais pas à ce qu'un membre d'une classe joue avec mon espace de noms de classe au-delà du simple ajout d'un membre. De plus, les modifications apportées à la structure anonyme peuvent affecter ma classe sans autorisation.

JonM
la source
1
En raison de la façon dont les structures / unions anonymes sont créées (c'est une syntaxe spéciale en ligne qui ne peut être cachée que par une macro), vous ne pouvez pas être surpris qu'un membre que vous utilisez soit un membre anonyme. Je ne pense donc pas que ce raisonnement ait un sens. La raison réelle est que les unions anonymes sont prises en charge en C ++, pour la compatibilité C uniquement. C ne prend pas en charge les structures anonymes (jusqu'à C11) et donc C ++ non plus.
bames53
1

Votre code

union {
  struct {
    float x;
    float y;
    float z;
  };
  float v[3];
};

est comme

union Foo {
   int;
   float v[3];
};

qui est sûrement invalide (en C99 et avant).

La raison est probablement de simplifier l'analyse (en C), car dans ce cas, il vous suffit de vérifier que le corps struct / union n'a que des "déclarations déclaratrices" comme

Type field;

Cela dit, gcc et "autres compilateurs" prennent en charge les champs sans nom comme extension.

Edit: Les structures anonymes sont désormais officiellement supportées en C11 (§6.7.2.1 / 13).

KennyTM
la source
5
Du point de vue de l'analyse, je ne pense pas que ce union { ... }soit différent de struct { ... }. Le premier est valable, mais le second ne l'est pas.
Johannes Schaub - litb
3
Étant donné à quel point le C ++ est absurdement difficile à analyser en général, je doute que la norme commise ne permette pas les structures et les unions sans nom juste pour simplifier l'analyse.
Adrian McCarthy
@Adrian: J'ai dit C, pas C ++. C ++ adopte la syntaxe de C et l'étend. Les créateurs de C ++ ne voient probablement pas le besoin d'autoriser les membres struct / union sans nom pour ne pas jouer avec cette partie de la syntaxe.
kennytm
@Adrian, Bon point là-bas Adrian, je n'ai toujours pas pensé que "trop ​​difficile à mettre en œuvre" serait jamais une préoccupation de Bjarne et de l'équipage
bobobobo
C et C ++ prennent tous deux en charge les unions sans nom, donc le commentaire qui union { ... };n'est pas valide n'est pas correct.
bames53