Pourquoi je ne peux pas initialiser static
les membres de données en classe?
La norme C ++ autorise uniquement l'initialisation de types intégraux ou énumération constants statiques à l'intérieur de la classe. C'est la raison a
pour laquelle il est permis d'être initialisé alors que d'autres ne le sont pas.
Référence:
C ++ 03 9.4.2 Membres de données statiques
§4
Si un membre de données statique est de type const intégrale ou énumération const, sa déclaration dans la définition de classe peut spécifier un initialiseur de constante qui doit être une expression constante intégrale (5.19). Dans ce cas, le membre peut apparaître dans des expressions constantes intégrales. Le membre doit toujours être défini dans une portée d'espace de noms s'il est utilisé dans le programme et la définition de portée d'espace de noms ne doit pas contenir d'initialiseur.
Quels sont les types intégraux?
C ++ 03 3.9.1 Types fondamentaux
§7
Les types bool, char, wchar_t et les types entiers signés et non signés sont collectivement appelés types intégraux.43) Un synonyme de type intégral est le type entier.
Note de bas de page:
43) Par conséquent, les énumérations (7.2) ne sont pas intégrales; cependant, les énumérations peuvent être promues en int, unsigned int, long ou unsigned long, comme spécifié dans 4.5.
Solution de contournement:
Vous pouvez utiliser l' astuce enum pour initialiser un tableau dans votre définition de classe.
class A
{
static const int a = 3;
enum { arrsize = 2 };
static const int c[arrsize] = { 1, 2 };
};
Pourquoi la norme ne permet-elle pas cela?
Bjarne l'explique bien ici :
Une classe est généralement déclarée dans un fichier d'en-tête et un fichier d'en-tête est généralement inclus dans de nombreuses unités de traduction. Cependant, pour éviter les règles compliquées de l'éditeur de liens, C ++ exige que chaque objet ait une définition unique. Cette règle serait rompue si C ++ autorisait la définition en classe d'entités qui devaient être stockées en mémoire en tant qu'objets.
Pourquoi seuls static const
les types intégraux et les énumérations sont-ils autorisés dans l'initialisation en classe?
La réponse est cachée dans la citation de Bjarne, lisez-la attentivement,
"C ++ exige que chaque objet ait une définition unique. Cette règle serait enfreinte si C ++ autorisait la définition en classe d'entités qui devaient être stockées en mémoire en tant qu'objets."
Notez que seuls les static const
entiers peuvent être traités comme des constantes de temps de compilation. Le compilateur sait que la valeur entière ne changera pas à tout moment et peut donc appliquer sa propre magie et appliquer des optimisations, le compilateur intègre simplement ces membres de classe, c'est-à-dire qu'ils ne sont plus stockés en mémoire, car le besoin d'être stocké en mémoire est supprimé , il donne à ces variables l'exception à la règle mentionnée par Bjarne.
Il est à noter ici que même si static const
les valeurs intégrales peuvent avoir une initialisation en classe, la prise d'adresse de ces variables n'est pas autorisée. On peut prendre l'adresse d'un membre statique si (et seulement si) il a une définition hors classe, ce qui valide encore le raisonnement ci-dessus.
les énumérations sont autorisées, car les valeurs d'un type énuméré peuvent être utilisées là où des entiers sont attendus. voir la citation ci-dessus
Comment cela change-t-il dans C ++ 11?
C ++ 11 assouplit la restriction dans une certaine mesure.
C ++ 11 9.4.2 Membres de données statiques
§3
Si un membre de données statique est de type littéral const, sa déclaration dans la définition de classe peut spécifier un initialiseur d'accolade ou d'égalité dans lequel chaque clause d'initialisation qui est une expression d'affectation est une expression constante. Un membre de données statique de type littéral peut être déclaré dans la définition de classe avec le constexpr specifier;
si oui, sa déclaration doit spécifier un initialiseur d'accolade ou d'égalité dans lequel chaque clause d'initialisation qui est une expression d'affectationest une expression constante. [Remarque: dans ces deux cas, le membre peut apparaître dans des expressions constantes. —End note] Le membre doit toujours être défini dans une portée d'espace de noms s'il est utilisé dans le programme et la définition de portée d'espace de noms ne doit pas contenir d'initialiseur.
En outre, C ++ 11 va permettre (§12.6.2.8) un élément de données non-statique pour être initialisé où il est déclaré (dans sa classe). Cela signifiera une sémantique utilisateur beaucoup plus simple.
Notez que ces fonctionnalités n'ont pas encore été implémentées dans la dernière version de gcc 4.7, vous risquez donc toujours d'avoir des erreurs de compilation.