38 votes

Initalisation de membres statiques C ++ (modèle amusant à l'intérieur)


Pour membre statique d'initialisation-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.

    Il fonctionne comme prévu

  • [1] sans commentaire:

    A = Bonjour, je suis A.
    B = 

    Je m'attends à ce 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)?

Merci beaucoup pour toutes suggestions et/ou commentaires.

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.

37voto

Johannes Schaub - litb Points 256113

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.

4voto

hirschhornsalz Points 16306

Le problème est que les définitions que vous donnez à la statique des variables membres sont des modèles trop.

template<class T>
std::string B<T>::mB;
template<class T>
typename B<T>::InitHelper B<T>::mInit;

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 un fichier source

template<>
typename B<int>::InitHelper B<int>::mInit;

Cela fonctionne essentiellement de la même manière explicitement la définition d'une classe template.

2voto

Alexey Malistov Points 13526
  • [1] cas non commenté: ça va. 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] cas non commenté: ça va. B<int>::getHelper() utilisation static InitHelper B<int>::mInit et mInit existe.

  • [1] a commenté, [2] non commenté: cela fonctionne pour moi dans VS2008.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X