Pourquoi ne puis-je pas initialiser un static
membre ou un static
tableau non const dans une classe?
class A
{
static const int a = 3;
static int b = 3;
static const int c[2] = { 1, 2 };
static int d[2] = { 1, 2 };
};
int main()
{
A a;
return 0;
}
le compilateur émet les erreurs suivantes:
g++ main.cpp
main.cpp:4:17: error: ISO C++ forbids in-class initialization of non-const static member ‘b’
main.cpp:5:26: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:5:33: error: invalid in-class initialization of static data member of non-integral type ‘const int [2]’
main.cpp:6:20: error: a brace-enclosed initializer is not allowed here before ‘{’ token
main.cpp:6:27: error: invalid in-class initialization of static data member of non-integral type ‘int [2]’
J'ai deux questions:
- Pourquoi ne puis-je pas initialiser
static
les membres de données en classe? - Pourquoi ne puis-je pas initialiser les
static
tableaux en classe, même leconst
tableau?
Réponses:
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
Quels sont les types intégraux?
C ++ 03 3.9.1 Types fondamentaux
§7
Note de bas de page:
Solution de contournement:
Vous pouvez utiliser l' astuce enum pour initialiser un tableau dans votre définition de classe.
Pourquoi la norme ne permet-elle pas cela?
Bjarne l'explique bien ici :
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
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.
la source
&member
reviendrait?static const char*
membre?Cela semble une relique de l'ancien temps des simples linkers. Vous pouvez utiliser des variables statiques dans des méthodes statiques comme solution de contournement:
et
et
construire:
courir:
Le fait que cela fonctionne (de manière cohérente, même si la définition de classe est incluse dans différentes unités de compilation), montre que l'éditeur de liens aujourd'hui (gcc 4.9.2) est en fait assez intelligent.
Drôle: impressions
0123
sur le bras et3210
sur x86.la source
Je pense que c'est pour vous empêcher de mélanger déclarations et définitions. (Pensez aux problèmes qui pourraient survenir si vous incluez le fichier à plusieurs endroits.)
la source
C'est parce qu'il ne peut y avoir qu'une seule définition de
A::a
celle que toutes les unités de traduction utilisent.Si vous avez joué
static int a = 3;
dans une classe dans un en-tête inclus dans toutes les unités de traduction, vous obtiendrez plusieurs définitions. Par conséquent, la définition non hors ligne d'un statique est forcée à générer une erreur de compilation.Utiliser
static inline
oustatic const
remédier à cela.static inline
Ne concrétise le symbole que s'il est utilisé dans l'unité de traduction et garantit que l'éditeur de liens sélectionne et ne laisse qu'une copie s'il est défini dans plusieurs unités de traduction en raison de son appartenance à un groupe comdat.const
at file scope fait que le compilateur n'émet jamais de symbole car il est toujours immédiatement substitué dans le code sauf s'ilextern
est utilisé, ce qui n'est pas autorisé dans une classe.Une chose à noter est qu'elle
static inline int b;
est traitée comme une définition alors questatic const int b
oustatic const A b;
est toujours traitée comme une déclaration et doit être définie hors ligne si vous ne la définissez pas à l'intérieur de la classe. Fait intéressant, ilstatic constexpr A b;
est traité comme une définition, alors questatic constexpr int b;
c'est une erreur et doit avoir un initialiseur (c'est parce qu'ils deviennent maintenant des définitions et comme toute définition const / constexpr à la portée du fichier, ils nécessitent un initialiseur qu'un int n'a pas mais un type de classe car il a un implicite= A()
lorsqu'il s'agit d'une définition - clang le permet mais gcc vous oblige à initialiser explicitement ou c'est une erreur. Ce n'est pas un problème avec inline à la place).static const A b = A();
n'est pas autorisé et doit êtreconstexpr
ouinline
afin de permettre à un initialiseur pour un objet statique de type classe, c'est-à-dire de faire un membre statique de type classe plus qu'une déclaration. Donc, oui dans certaines situations, ceA a;
n'est pas la même chose que l'initialisation expliciteA a = A();
(la première peut être une déclaration mais si seule une déclaration est autorisée pour ce type, la seconde est une erreur. Cette dernière ne peut être utilisée que sur une définition. Enconstexpr
fait une définition ). Si vous utilisezconstexpr
et spécifiez un constructeur par défaut, le constructeur devra êtreconstexpr
Un membre statique est une déclaration de portée de fichier pure et simple
extern int A::a;
(qui ne peut être faite que dans la classe et les définitions hors ligne doivent faire référence à un membre statique d'une classe et doivent être des définitions et ne peuvent pas contenir d'extern) alors qu'un membre non statique fait partie de la définition de type complète d'une classe et ont les mêmes règles que les déclarations de portée de fichier sansextern
. Ce sont implicitement des définitions. Il enint i[]; int i[5];
va de même pour une redéfinition alorsstatic int i[]; int A::i[5];
que contrairement à 2 externs, le compilateur détectera toujours un membre en double si vous le faitesstatic int i[]; static int i[5];
dans la classe.la source
les variables statiques sont spécifiques à une classe. Les constructeurs initialisent les attributs ESPECIALY pour une instance.
la source