Question

J'ai une classe de modèle définie dans un fichier d'en-tête comme celui-ci. Ici, j'ai également défini une variable statique:

#ifndef TEST1_H_
#define TEST1_H_

void f1();

static int count;

template <class T>
class MyClass
{
public:

    void f()
    {
        ++count;
    }


};

#endif

Et j'ai défini la fonction main () dans un autre fichier cpp comme ceci:

int main(int argc, char* argv[])
{
    MyClass<int> a;
    a.f();
    f1();

    cout<<"Main:" << count << "\n";

    return 0;
}

J'ai implémenté la fonction f1 () dans un autre fichier cpp comme ceci:

void f1()
{
    MyClass<int> a;
    a.f();

    cout<<"F1: " <<count <<"\n";
}

Quand j’ai compilé cela avec VC6, j’ai obtenu le résultat sous la forme & "; F1: 0 Main: 2 &"; Comment est-ce possible? De manière générale, comment dois-je gérer si je veux utiliser des variables statiques avec des modèles?

Était-ce utile?

La solution

Vous obtenez deux copies de la même variable parce que vous avez déclaré une variable statique dans un fichier d'en-tête. Lorsque vous déclarez une variable globale static de cette manière, vous indiquez qu'elle est locale à l'unité de compilation (le fichier .o). Puisque vous incluez l'en-tête dans deux unités de compilation, vous obtenez deux copies de count.

Je pense que ce que vous voulez vraiment ici est une variable membre de modèle statique associée à chaque instance de la classe de modèle. Cela ressemblerait à ceci:

template <class T>
class MyClass
{
    // static member declaration
    static int count;
    ...
};

// static member definition
template<class T> int MyClass<T>::count = 0;

Cela vous donnera un compte pour chaque instanciation de votre modèle. Autrement dit, vous aurez un compte pour MyClass<int>, MyClass<foo>, MyClass<bar>, etc. f1() ressemblerait maintenant à ceci:

void f1() {
    MyClass<int> a;
    a.f();

    cout<<"F1: " << MyClass<int>::count <<"\n";
}

Si vous souhaitez compter sur toutes les instanciations de MyClass (quels que soient leurs paramètres de modèle), vous devez utiliser une variable globale .

Cependant, vous ne voulez probablement pas de variable globale directement car vous courez le risque de l’utiliser avant qu’elle ne soit initialisée. Vous pouvez contourner ce problème en créant une méthode statique globale qui renvoie une référence à votre nombre:

int& my_count() {
    static int count = 0;
    return count;
}

Puis accédez-y depuis votre classe de la manière suivante:

void f() {
    ++my_count();
}

Cela garantira que le nombre est initialisé avant son utilisation, quelle que soit l'unité de compilation à partir de laquelle vous y accédez. Consultez la FAQ C ++ sur l'ordre d'initialisation statique pour plus de détails. .

Autres conseils

Si vous placez la déclaration statique dans un fichier d’en-tête, chaque fichier .cpp aura sa propre version de la variable. Les deux déclarations de cout impriment donc des variables différentes.

Vous attendiez-vous à " F1: 1 Principal: 1 " Vous avez instancié MyClass<int> deux unités de traduction distinctes (c’est-à-dire deux fichiers objets) et l’éditeur de liens a constaté qu’il existait une instanciation de modèle dupliquée. Il a donc ignoré l’instanciation contenue dans le fichier objet de f1.

Passez-vous /OPT:ICF ou /OPT:REF à l'éditeur de liens VC6? Cela pourrait être lié à la suppression de l’instanciation de modèles en double (ou non; les instanciations de modèles en double pourraient être un cas particulier, par rapport aux fonctions de duplication ordinaires). GCC semble faire quelque chose de similaire sur certaines plates-formes.

Quoi qu'il en soit, je ne compterais pas sur la cohérence de ce comportement entre les compilateurs. En outre, le fait de modifier l'ordre des fichiers objet sur la ligne de commande de l'éditeur de liens peut avoir une incidence sur l'instanciation supprimée.

Il existe une autre solution: vous pouvez créer une classe parent partagée et y insérer cette variable statique, puis faire en sorte que votre classe de modèle l’hérite de manière privée. Voici un exemple:

class Parent
{
protected: 
    static long count;
};

long Parent::count = 0;

template<typename T>
class TemplateClass: private Parent
{
private: 
    int mKey;
public:
    TemplateClass():mKey(count++){}
    long getKey(){return mKey;}
}

int main()
{
    TemplateClass<int> obj1;
    TemplateClass<double> obj2;

    std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl;
    std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl;

    return 0;
}

La sortie sera:

Object 1 key is: 0 
Object 2 key is: 1

Je pense que c'est en fait un comportement non défini .

Selon C ++ 14 [basic.def.odr] / 6:

  

Il peut exister plusieurs définitions d'une fonction [...] membre d'un modèle de classe dans un programme [...] à condition que chaque définition apparaisse dans une unité de traduction différente et que les définitions satisfassent aux exigences suivantes . Étant donné une telle entité nommée D définie dans plusieurs unités de traduction,

     
      
  • chaque définition de D doit consister en la même séquence de jetons; et
  •   
  • dans chaque définition de D, les noms correspondants recherchés conformément à 3.4 doivent faire référence à une entité définie dans la définition de D ou faire référence à la même entité, après résolution de la surcharge (13.3) et après mise en correspondance du modèle partiel spécialisation (14.8.3), sauf qu'un nom peut faire référence à un   Objet const avec un lien interne ou inexistant si l'objet a le même type littéral dans toutes les définitions de D et si l'objet est initialisé avec une expression constante (5.19), si l'objet n'est pas utilisé, et si l'objet a la même valeur dans toutes les définitions de D; [...]
  •   

Le problème est que dans le premier .cpp fichier, le nom count situé dans f1 désigne un objet différent du nom static situé dans <=> dans le deuxième fichier <=>, violant ainsi le condition que les noms correspondants fassent référence à la même entité.

Ce sont des objets différents en raison du <=> spécificateur qui indique que chaque unité de traduction obtient son propre objet avec ce nom.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top