Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Lorsque vous définissez un modèle de classe, vous devez organiser le code source de sorte que les définitions de membres soient visibles par le compilateur en cas de besoin. Vous avez le choix d’utiliser le modèle d’inclusion ou le modèle d’instanciation explicite. Dans le modèle d’inclusion, vous incluez les définitions de membres dans chaque fichier qui utilise un modèle. Cette approche est la plus simple et offre une flexibilité maximale en termes de types concrets compatibles avec votre modèle. Son inconvénient est qu’il peut augmenter le délai de compilation. Les temps peuvent être significatifs si un projet ou les fichiers inclus eux-mêmes sont volumineux. Avec l’instanciation explicite, le modèle lui-même instancie les classes ou les membres de classe concrets pour des types spécifiques. Cette approche peut accélérer le délai de compilation, mais elle limite l’utilisation aux seules classes que l’implémenteur de modèle a activées à l’avance. En règle générale, nous vous recommandons d’utiliser le modèle d’inclusion, sauf si le délai de compilation pose problème.
Background
Les modèles ne sont pas comme des classes ordinaires dans le sens où le compilateur ne génère pas de code objet pour un modèle ou l’un de ses membres. Il n’y a rien à générer tant que le modèle n’est pas instancié avec des types concrets. Lorsque le compilateur rencontre une instanciation de modèle comme MyClass<int> mc; et qu’il n’existe encore aucune classe avec cette signature, il génère une nouvelle classe. Il tente également de générer le code des fonctions d’un membre qui sont utilisées. Si ces définitions se trouvent dans un fichier qui n’est pas #included, directement ou indirectement, dans le fichier .cpp qui est compilé, le compilateur ne peut pas les voir. Du point de vue du compilateur, il n’est pas nécessairement une erreur. Les fonctions peuvent être définies dans une autre unité de traduction où l’éditeur de liens les trouvera. Si l’éditeur de liens ne trouve pas ce code, il génère une erreur externe non résolue.
Le modèle d’inclusion
La façon la plus simple et la plus courante d’afficher les définitions de modèle dans une unité de traduction consiste à placer les définitions dans le fichier d’en-tête. Tout .cpp fichier qui utilise le modèle doit simplement à #include l’en-tête. Cette approche est utilisée dans la bibliothèque standard.
#ifndef MYARRAY
#define MYARRAY
#include <iostream>
template<typename T, size_t N>
class MyArray
{
T arr[N];
public:
// Full definitions:
MyArray(){}
void Print()
{
for (const auto v : arr)
{
std::cout << v << " , ";
}
}
T& operator[](int i)
{
return arr[i];
}
};
#endif
Avec cette approche, le compilateur a accès à la définition complète du modèle et peut instancier des modèles à la demande pour n’importe quel type. Il est simple et relativement facile à maintenir. Mais le modèle d’inclusion présente un certain coût en termes de délai de compilation. Ce coût peut être significatif dans des programmes volumineux, surtout si l’en-tête du modèle lui-même inclut d’autres en-têtes. Chaque .cpp fichier qui #includes l’en-tête obtient sa propre copie des modèles de fonction et toutes les définitions. L’éditeur de liens sera généralement en mesure de trier les choses afin que vous ne finissent pas par plusieurs définitions pour une fonction, mais il faut du temps pour effectuer ce travail. Dans les programmes plus petits, ce délai de compilation supplémentaire sera sans doute marginal.
Le modèle d’instanciation explicite
Si le modèle d’inclusion n’est pas viable pour votre projet et que vous connaissez définitivement l’ensemble de types qui seront utilisés pour instancier un modèle, vous pouvez séparer le code du modèle dans un .h fichier et .cpp dans le .cpp fichier instancier explicitement les modèles. Cette approche génère du code objet que le compilateur verra lorsqu’il rencontre des instanciations utilisateur.
Vous créez une instanciation explicite à l’aide du mot clé template suivi de la signature de l’entité que vous souhaitez instancier. Cette entité peut être un type ou un membre. Si vous instanciez explicitement un type, tous les membres sont instanciés.
Le fichier d’en-tête MyArray.h déclare la classe MyArrayde modèle :
//MyArray.h
#ifndef MYARRAY
#define MYARRAY
template<typename T, size_t N>
class MyArray
{
T arr[N];
public:
MyArray();
void Print();
T& operator[](int i);
};
#endif
Le fichier MyArray.cpp source instancie template MyArray<double, 5> explicitement et template MyArray<string, 5>:
//MyArray.cpp
#include <iostream>
#include "MyArray.h"
using namespace std;
template<typename T, size_t N>
MyArray<T,N>::MyArray(){}
template<typename T, size_t N>
void MyArray<T,N>::Print()
{
for (const auto v : arr)
{
cout << v << "'";
}
cout << endl;
}
template MyArray<double, 5>;
template MyArray<string, 5>;
Dans l’exemple précédent, les instanciations explicites se trouvent en bas du .cpp fichier. Un MyArray peut être utilisé uniquement pour ou pour double les String types.
Remarque
En C++11, le export mot clé a été déconseillé dans le contexte des définitions de modèle. Dans la pratique, cela a un impact limité car la plupart des compilateurs ne l’ont jamais pris en charge.