C++ Statique de l'initialisation de membre (modèle de plaisir à l'intérieur)
Statique de l'initialisation de membre-je utiliser un imbriquée helper struct, qui fonctionne très bien pour les non basées sur des modèles de classes.
Cependant, si la classe englobante est paramétrée par un modèle, imbriquées l'initialisation de la classe n'est pas instancié, si l'objet d'assistance n'est pas accessible dans le code principal.
Pour illustration, un exemple simplifié (Dans mon cas, j'ai besoin d'initialiser un vecteur).
#include <string>
#include <iostream>
struct A
{
struct InitHelper
{
InitHelper()
{
A::mA = "Hello, I'm A.";
}
};
static std::string mA;
static InitHelper mInit;
static const std::string& getA(){ return mA; }
};
std::string A::mA;
A::InitHelper A::mInit;
template<class T>
struct B
{
struct InitHelper
{
InitHelper()
{
B<T>::mB = "Hello, I'm B."; //[3]
}
};
static std::string mB;
static InitHelper mInit;
static const std::string& getB() { return mB; }
static InitHelper& getHelper(){ return mInit; }
};
template<class T>
std::string B<T>::mB; //[4]
template<class T>
typename B<T>::InitHelper B<T>::mInit;
int main(int argc, char* argv[])
{
std::cout << "A = " << A::getA() << std::endl;
// std::cout << "B = " << B<int>::getB() << std::endl; //[1]
// B<int>::getHelper(); //[2]
}
Avec g++ 4.4.1:
-
[1] et [2], a commenté:
A = Bonjour, je suis A.
Fonctionne comme prévu
-
[1] sans commentaire:
A = Bonjour, je suis A. B =
Je l'espère, que le InitHelper initialise mo
- [1] et [2] sans commentaire:
A = Bonjour, je suis A. B = Bonjour, je suis B.
Il fonctionne comme prévu
- [1], a commenté, [2] sans commentaire:
Erreur dans l'initialisation statique stade [3]
Donc ma question: Est-ce un bug du compilateur ou est le bug assis entre le moniteur et le président?
Et si ce dernier est le cas: Est-il une solution élégante (c'est à dire sans appeler explicitement une initialisation statique méthode)?
I Mise À Jour:
Cela semble être un comportement souhaité (tel que défini dans la norme ISO/IEC C++ standard 2003, 14.7.1):
À moins qu'un membre d'un modèle de classe ou un membre de modèle a été instancié explicitement ou explicitement spécialisés, la spécialisation du membre est automatiquement instanciés lorsque la spécialisation est référencé dans un contexte qui exige la définition du membre d'exister; 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.
- Visual Studio 2008 a le même comportement (-erreur dans l'initialisation statique)
- Pourquoi ne pas simplement écrire std::string B<T>::mo = "Bonjour, je suis B."?
- Ok, je vois que dans le cas où vous avez besoin de inialize un vecteur, désolé.
- Insérez
mB
définition dansInitHelper
.
Vous devez vous connecter pour publier un commentaire.
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:
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
14.7.1
: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 (au
3.2/2
). En particulier, si toutes les références sont de uninstantiated modèles, les membres d'un modèle ou d'unsizeof
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é.Implicite de l'instanciation par
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.
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
3.6.2/1
s'applique, ce qui en dit (accent mis par moi):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 un l'instanciation de l'unité de, comme l'explique l'
2.1/1
: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é dans les
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 à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
3.6.2
à la suivante:Ce moyen de votre code, que:
[1]
et[2]
a 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 pour l'instanciation deB<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 utiliseB<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 utiliseB<int>::mInit
, de sorte qu'il n'est pas instancié, et donc pas un objet deB<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 à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 - encore une fois, les deux les données membres statiques sont utilisé:B<int>::mInit
est utilisé parB<int>::getHelper
, et l'instanciation deB<int>::mInit
sera la cause de son constructeur pour être instancié, qui utiliseraB<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 initialiseB<int>::mInit
première, qui fonctionnera sur n'est pas encore construit objet de type string.Le problème est que les définitions que vous donnez à la statique des variables membres sont des modèles trop.
Lors de la compilation, ce qui définit en fait rien, puisque T n'est pas connue. C'est quelque chose comme une déclaration de classe ou d'un modèle de définition, le compilateur ne génère pas de code ou de stockage de réserve quand il la voit.
La définition qui se passe implicitement plus tard, lorsque vous utilisez la classe de modèle. Parce que dans le segfaulting le cas où vous n'utilisez pas de B<int>::mInit, il n'est jamais créé.
Une solution serait explicitement la définition du besoin membre (sans l'initialiser): Mettre quelque part le fichier source d'un
Cela fonctionne essentiellement de la même manière explicitement la définition d'une classe template.
B<int>::getHelper()
, qui utiliseB<int>::mInit
.[1] décommenté cas:
Il est ok.
static InitHelper B<int>::mInit
n'existe pas. Si le membre de la classe de modèle (struct) n'est pas utilisé, il n'est pas compilé.[1] et [2] décommenté cas:
Il est ok.
B<int>::getHelper()
utilisationstatic InitHelper B<int>::mInit
etmInit
existe.[1], a commenté, [2] sans commentaire:
il fonctionne pour moi dans VS2008.