Cela a été discuté sur usenet il y a quelques temps, alors que j'essayais de répondre à une autre question sur stackoverflow: Point de l'Instanciation de Données Membres Statiques. Je pense que ça vaut la réduction de l'épreuve de cas, et compte tenu de chaque scénario dans l'isolement, donc, nous allons regarder cela de plus en général le premier:
struct C { C(int n) { printf("%d\n", n); } };
template<int N>
struct A {
static C c;
};
template<int N>
C A<N>::c(N);
A<1> a; // implicit instantiation of A<1> and 2
A<2> b;
Vous avez la définition d'une donnée membre statique du modèle. Ce n'est pas encore créer toutes les données membres, en raison de l' 14.7.1
:
"... en particulier, l'initialisation (et de tous les effets indésirables liés) d'une donnée membre statique ne pas se produire à moins que la donnée membre statique est utilisée d'une manière qui exige la définition de la donnée membre statique d'exister."
La définition de quelque chose (= entité) est nécessaire lorsque l'entité est "utilisé", selon la définition de la règle qui définit ce mot (en 3.2/2
). En particulier, si toutes les références sont de uninstantiated modèles, les membres d'un modèle ou d'un sizeof
des expressions ou des choses semblables qui ne sont pas "utiliser" l'entité (puisqu'ils ne sont pas potentiellement de les évaluer, ou simplement, ils n'existent pas encore en tant que fonctions/fonctions membres qui sont en lui-même utilisé), par exemple une donnée membre statique n'est pas instancié.
Une instanciation implicite en 14.7.1/7
instancie les déclarations de données membres statiques - c'est-à-dire, il va instancier le modèle nécessaire pour le traitement de cette déclaration. Il n'est pas, cependant, d'instancier des définitions, c'est-à-dire, les initialiseurs ne sont pas instanciés et les constructeurs du type de celle donnée membre statique ne sont pas définis implicitement (marqué comme utilisé).
Que tous les moyens, le code ci-dessus affichera rien encore. Nous allons cause implicite des instanciations des données membres statiques maintenant.
int main() {
A<1>::c; // reference them
A<2>::c;
}
Cela va entraîner les deux données membres statiques d'exister, mais la question est - comment est l'ordre de l'initialisation? Sur une simple lecture, on pourrait penser que l' 3.6.2/1
s'applique, ce qui en dit (accent mis par moi):
"Les objets statiques de stockage durée définie dans l'espace de noms de champ dans la même unité de traduction et dynamique initialisé doit être initialisé dans l'ordre de leur définition s'affiche dans l'unité de traduction."
Maintenant, comme dit dans le message usenet et expliqué dans ce rapport de défaut, ces données membres statiques ne sont pas définis dans une unité de traduction, mais ils sont instanciés dans une instanciation de l'unité, comme l'explique l' 2.1/1
:
Chaque traduits de l'unité de traduction est examiné afin de produire une liste des instanciations. [Note: ce peut être notamment les instanciations qui ont été explicitement demandé (14.7.2). ] Les définitions des modèles requis sont situés. Elle est mise en œuvre-définir si la source de la traduction des unités contenant ces définitions est tenu d'être disponible. [Note: une mise en œuvre pourrait coder les informations suffisantes sur l'traduit de l'unité de traduction de manière à s'assurer que la source n'est pas nécessaire ici. ] Toutes les instanciations sont effectuées pour produire de l'instanciation d'unités. [Note: ces sont similaires à traduire les unités de traduction, mais contiennent pas de références à uninstantiated modèles et aucun modèle de définitions. ] Le programme est mal formé si une instanciation échoue.
Le Point de l'Instanciation d'un tel membre, aussi, n'a pas vraiment d'importance, car un tel point de l'instanciation est le contexte de lien entre une instanciation et de ses unités de traduction - il définit les déclarations qui sont visibles (comme spécifié à l' 14.6.4.1
, et chacun de ces point de instanciations doit donner des instanciations le même sens, comme spécifié dans la définition de la règle à l' 3.2/5
, dernier alinéa).
Si nous voulons nous a ordonné d'initialisation, nous avons à organiser de sorte que nous ne plaisante pas avec les instanciations, mais avec des déclarations explicites - c'est le domaine de l'explicite spécialisations, comme ce ne sont pas vraiment différentes de la normale déclarations. En fait, C++0x changé son libellé de l' 3.6.2
à la suivante:
Dynamique de l'initialisation d'un objet statique durée de stockage est ordonné ou désordonné.
Définitions explicitement classe spécialisée modèle de données membres statiques ont ordonné à l'initialisation. D'autres
classe de modèle aux données membres statiques (c'est à dire, implicitement ou explicitement instancié spécialisations) ont non ordonnée d'initialisation.
Ce moyen de votre code, que:
-
[1]
et [2]
commenté: Pas de référence pour les données membres statiques existent, de sorte que leurs définitions (et aussi pas leurs déclarations, car il n'est pas nécessaire au moment de l'instanciation de l' B<int>
) ne sont pas instanciés. Aucun effet secondaire se produit.
-
[1]
sans commentaire: B<int>::getB()
est utilisé, ce qui en elle-même utilise B<int>::mB
, qui exige que le membre statique d'exister. La chaîne est initialisée avant la main (en tout cas avant cette déclaration, dans le cadre de l'initialisation de la non-locales des objets). Rien utilise B<int>::mInit
, de sorte qu'il n'est pas instancié, et donc pas d'objet B<int>::InitHelper
est jamais créé, ce qui rend son constructeur n'étant pas utilisé, ce qui à son tour ne sera jamais céder quelque chose d' B<int>::mB
: Vous aurez juste sortie d'une chaîne vide.
-
[1]
et [2]
sans commentaire: Que ce qui a fonctionné pour vous est de la chance (ou l'inverse :)). Il n'est pas nécessaire pour un ordre particulier pour les appels d'initialisation, comme expliqué ci-dessus. Il peut travailler sur VC++, échouer sur GCC et de travailler sur clang. Nous ne savons pas.
-
[1]
commenté, [2]
sans commentaire: Même problème à nouveau, à la fois les données membres statiques sont utilisés: B<int>::mInit
est utilisée par B<int>::getHelper
, et l'instanciation d' B<int>::mInit
sera la cause de son constructeur pour être instancié, qui utilisera B<int>::mB
- mais pour votre compilateur, l'ordre est différent dans cette série particulière (comportement non spécifié n'est pas nécessaire d'être cohérent entre les différentes pistes): Il initialise B<int>::mInit
première, qui fonctionnera sur n'est pas encore construit objet de type string.