45 votes

Détails de l'instanciation des templates des compilateurs GCC et MS

Quelqu'un pourrait-il fournir une comparaison ou des détails spécifiques sur la façon dont l'instanciation de modèle au moment de la compilation et/ou de la liaison dans les compilateurs GCC et MS ? Ce processus est-il différent dans le contexte des bibliothèques statiques, des bibliothèques partagées et des exécutables ? J'ai trouvé ce document sur la façon dont GCC le gère mais je ne suis pas sûr que l'information fait toujours référence à l'état actuel des choses. Devrais-je utiliser les drapeaux qu'ils suggèrent ici lors de la compilation de mes bibliothèques, par exemple -fno-implicit-templates ?

Ce que je sais (et qui n'est pas forcément exact), c'est que.. :

  • les modèles seront instanciés lorsqu'ils seront utilisés.
  • les modèles seront instanciés à la suite d'instanciations explicites
  • les instanciations dupliquées sont généralement gérées en repliant les instanciations dupliquées ou en reportant l'instanciation jusqu'au moment de la liaison.

57voto

phresnel Points 20082

Point d'instanciation

les modèles seront instanciés lorsqu'ils seront utilisés.

Pas exactement, mais en gros. Le point précis d'instanciation est un peu subtil, et je vous délègue à la section nommée Point d'instanciation dans le beau livre de Vandevoorde/Josuttis.

Cependant, les compilateurs n'implémentent pas nécessairement les POIs correctement : Bogue c++/41995 : Point d'instanciation incorrect pour le modÃ?le de fonction.


Instanciation partielle

les modèles seront instanciés lorsqu'ils seront utilisés.

C'est partiellement correct. C'est vrai pour les modèles de fonction, mais pour les modèles de classe, seules les fonctions membres qui sont utilisées sont instanciées. Le code suivant est bien formé :

#include <iostream>

template <typename> struct Foo {
    void let_me_stay() {
        this->is->valid->code. get->off->my->lawn;
    }

    void fun() { std::cout << "fun()" << std::endl; } 
};

int main () {
    Foo<void> foo;
    foo.fun();
}

let_me_stay() est vérifié syntaxiquement (et la syntaxe y est correcte), mais pas sémantiquement (c'est-à-dire qu'il n'est pas interprété).


Recherche biphasée

Cependant, seuls code dépendant est interprété plus tard ; clairement, dans Foo<> , this dépend de l'identifiant exact du modèle avec lequel l'application Foo<> est instancié, nous avons donc reporté le contrôle d'erreur de l'option Foo<>::let_me_alone() jusqu'au moment de l'instanciation.

Mais si nous n'utilisons pas quelque chose qui dépend de l'instanciation spécifique, le code doit être bon. Par conséquent, ce qui suit est no bien formé :

$ cat non-dependent.cc
template <typename> struct Foo {
    void I_wont_compile() { Mine->is->valid->code. get->off->my->lawn; }
};
int main () {} // note: no single instantiation

Mine est un symbole totalement inconnu du compilateur, contrairement à this pour lesquels le compilateur peut déterminer la dépendance d'instance.

Le point clé ici est que C++ utilise un modèle de lookup biphasé Il s'agit d'un concept souvent mal compris ou inconnu, de nombreux programmeurs C++ supposent que les modèles ne sont pas analysés du tout avant l'instanciation, mais ce n'est qu'un mythe provenant de Microsoft C++).


Instanciation complète des modèles de classe

La définition de Foo<>::let_me_stay() a fonctionné parce que la vérification des erreurs a été reportée à plus tard, comme pour les this le pointeur, qui est dépendant. Sauf dans le cas où vous auriez fait usage de

instanciations explicites

cat > foo.cc
#include <iostream>

template <typename> struct Foo {
    void let_me_stay() { this->is->valid->code. get->off->my->lawn; }
    void fun() { std::cout << "fun()" << std::endl; } 
};

template struct Foo<void>;
int main () {
    Foo<void> foo;
    foo.fun();
}

g++ foo.cc
error: error: ‘struct Foo<void>’ has no member named ‘is’

Définitions de modèles dans différentes unités de traduction

Lorsque vous instanciez explicitement, vous instanciez explicitement. Et faire tous visibles par l'éditeur de liens, ce qui signifie également que la définition du modèle peut résider dans différentes unités de traduction :

$ cat A.cc
template <typename> struct Foo {
    void fun();  // Note: no definition
};
int main () {
    Foo<void>().fun();
}

$ cat B.cc
#include <iostream>
template <typename> struct Foo {
    void fun();

};
template <typename T>
void Foo<T>::fun() { 
    std::cout << "fun!" << std::endl;
}  // Note: definition with extern linkage

template struct Foo<void>; // explicit instantiation upon void

$ g++ A.cc B.cc
$ ./a.out
fun!

Cependant, vous devez explicitement instancier pour que tous les arguments du modèle soient utilisés, autrement

$ cat A.cc
template <typename> struct Foo {
    void fun();  // Note: no definition
};
int main () {
    Foo<float>().fun();
}
$ g++ A.cc B.cc
undefined reference to `Foo<float>::fun()'

Petite remarque sur le lookup biphasé : La norme ne dicte pas si un compilateur implémente effectivement la recherche biphasée. Pour être conforme, cependant, il devrait fonctionner comme si c'était le cas (tout comme l'addition ou la multiplication ne doivent pas nécessairement être effectuées en utilisant des instructions CPU d'addition ou de multiplication.

-2voto

Qwertie Points 5311

Editar : Il s'avère que ce que j'ai écrit ci-dessous est contraire à la norme C++. C'est vrai pour Visual C++, mais faux pour les compilateurs qui utilisent le "two-phase name lookup".

Pour autant que je sache, ce que vous dites est correct. Les modèles seront instanciés lorsqu'ils seront effectivement utilisés (y compris lorsqu'ils sont déclarés comme membres d'un autre type, mais pas lorsqu'ils sont mentionnés dans une déclaration de fonction (qui n'a pas de corps)) ou à la suite d'instanciations explicites.

Un problème avec les modèles est que si vous utilisez le même modèle (par exemple un vecteur) dans plusieurs unités de compilation différentes (fichiers .cpp), le compilateur répète le travail d'instanciation du modèle dans chaque fichier .cpp, ce qui ralentit la compilation. IIRC, GCC a un mécanisme (non-standard ?) qui peut être utilisé pour éviter cela (mais je n'utilise pas GCC). Mais Visual C++ répète toujours ce travail, à moins que vous n'utilisiez l'instanciation explicite du template dans un header précompilé (mais même cela ralentira votre compilation, puisqu'un fichier PCH plus grand prend plus de temps à charger). Ensuite, le linker élimine les doublons. Nota : un commentaire ci-dessous lié à un page ce qui nous indique que tous les compilateurs ne fonctionnent pas de cette manière. Certains compilateurs reportent l'instanciation des fonctions au moment de la liaison, ce qui devrait être plus efficace.

Un modèle n'est pas entièrement instancié lors de sa première utilisation. En particulier, les fonctions du modèle ne sont pas instanciées tant qu'elles ne sont pas réellement appelées. Vous pouvez facilement vérifier cela en ajoutant une fonction absurde à un modèle que vous utilisez activement :

void Test() { fdsh "s.w" = 6; wtf? }

Vous n'obtiendrez pas d'erreur si vous n'instanciez pas explicitement le modèle ou si vous n'essayiez pas d'appeler la fonction.

Je m'attends à ce que les bibliothèques statiques (et les fichiers objets) stockent le code objet de tous les modèles qui ont été instanciés. Mais si votre programme dépend d'une certaine bibliothèque statique, vous ne pouvez pas réellement appeler les fonctions du modèle qui ont déjà été instanciées dans celle-ci, du moins pas en VC++, qui a toujours besoin du code source (avec les corps de fonction) d'une classe modèle pour appeler les fonctions de celle-ci.

Je ne pense pas qu'il soit possible d'appeler une fonction template dans une bibliothèque partagée (lorsque vous n'avez pas le code source de la fonction template que vous voulez appeler).

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