Variable statique de modèle

Je ne peux pas comprendre pourquoi, si nous définissons une variable statique de classe habituelle (non-template) dans l’en-tête, nous avons une erreur de linker, mais dans le cas des templates, nous aurons :

C’est l’en-tête du template (template.h):

// template.h template class Templ { public: static int templStatic; }; template Templ::templStatic = 0; 

C’est la première unité utilisant le template (unit1.cpp)

 // unit1.cpp #include "template.h" int method1() { return Templ::templStatic++; } 

Deuxième unité ici (unit2.cpp):

 // unit2.cpp #include "template.h" int method2() { return Templ::templStatic++; } 

Et enfin, main.cpp:

 // main.cpp #include  int method1(); int method2(); int main(int argc, char** argv) { std::cout << method1() << std::endl; std::cout << method2() << std::endl; } 

Après avoir compilé, lié et exécuté ce code, nous aurons la sortie suivante:

 0 1 

Alors, pourquoi, dans le cas de modèles, tout fonctionne bien (et comme prévu)? Comment compilateur ou éditeur de liens gère-t-il ceci (nous pouvons comstackr chaque fichier .cpp dans un appel séparé du compilateur, puis les lier à caling to linker, donc le compilateur et l’éditeur de liens ne “voient” pas tous les fichiers .cpp en même temps)?

PS: Mon compilateur: msvcpp 9 (mais vérifié aussi sur mingw)

C’est parce que la définition du membre de données statique est elle-même un modèle. Cela est nécessaire pour la même raison que vous êtes autorisé à avoir un modèle de fonction qui n’est pas intégré plusieurs fois dans un programme. Vous avez besoin du modèle pour générer l’entité résultante (par exemple, une fonction ou un membre de données statique). Si vous n’êtes pas autorisé à définir un membre de données statique, comment instanciez-vous les éléments suivants?

 template struct F { static int const value; }; template int const F::value = sizeof(T); 

On ne sait pas ce que T is – la norme indique que la définition en dehors du modèle de classe est une définition de modèle, dans laquelle les parameters sont hérités du propriétaire de son modèle de classe.


J’ai fait des expériences avec GCC. Dans ce qui suit, nous avons une instanciation implicite de F::value et une spécialisation explicite de F::value qui doit être définie dans un fichier .cpp pour ne pas provoquer des erreurs de symbole dupliquées lorsqu’elles sont incluses plusieurs fois. .

 // Translation Unit 1 template struct F { static int value; }; template int F::value = sizeof(T); // this would belong into a .cpp file template<> int F::value = 2; // this implicitly instantiates F::value int test = F::value; int main() { } 

La deuxième unité de traduction contient juste une autre instanciation implicite du même membre de données statique

 template struct F { static int value; }; template int F::value = sizeof(T); int test1 = F::value; 

Voici ce que nous obtenons avec GCC – il rend chaque instanciation implicite dans un symbole faible et la place ici dans sa propre section. Les symboles faibles ne provoqueront pas d’erreurs lorsque plusieurs d’entre eux existent au moment de la liaison. Au lieu de cela, l’éditeur de liens choisit une instance et supprime les autres en supposant qu’elles sont toutes identiques.

 objdump -Ct main1.o # => # cut down to the important ones 00000000 l df *ABS* 00000000 main1.cpp 0000000a l F .text 0000001e __static_initialization_and_destruction_0(int, int) 00000000 ld .data._ZN1FIfE5valueE 00000000 .data._ZN1FIfE5valueE 00000028 l F .text 0000001c global constructors keyed to _ZN1FIcE5valueE 00000000 g O .data 00000004 F::value 00000000 g O .bss 00000004 test 00000000 g F .text 0000000a main 00000000 w O .data._ZN1FIfE5valueE 00000004 F::value 

Donc, comme nous pouvons le voir, F::value est un symbole faible, ce qui signifie que l’éditeur de liens peut en voir plusieurs au moment du lien. test , main et F::value sont des symboles globaux (non faibles). Reliant ensemble main1.o et main2.o , on voit dans la sortie de la carte ( -Wl,-M ) ce qui suit

 # (mangled name) .data._ZN1FIfE5valueE 0x080497ac 0x4 main1.o 0x080497ac F::value 

Cela indique que, en réalité, il laisse tomber toutes les instances sauf une.

Il existe une solution, vous pouvez créer une classe parente et y placer la variable statique, puis en faire hériter votre classe de modèle en privé, voici un exemple:

 class Parent { protected: static long count; }; long Parent::count = 0; template class TemplateClass: private Parent { private: int mKey; public: TemplateClass():mKey(count++){} long getKey(){return mKey;} } int main() { TemplateClass obj1; TemplateClass obj2; std::cout<<"Object 1 key is: "< 

La sortie sera:

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