web-dev-qa-db-fra.com

en utilisant un modèle externe (C ++ 11)

Figure 1: modèles de fonction

TemplHeader.h

template<typename T>
void f();

TemplCpp.cpp

template<typename T>
void f(){
   //...
}    
//explicit instantation
template void f<T>();

Main.cpp

#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
    f<char>();
    return 0;
}

Est-ce la bonne façon d'utiliser extern template ou utilise-t-on ce mot-clé uniquement pour les modèles de classe comme dans la figure 2?

Figure 2: modèles de classe

TemplHeader.h

template<typename T>
class foo {
    T f();
};

TemplCpp.cpp

template<typename T>
void foo<T>::f() {
    //...
}
//explicit instantation
template class foo<int>;

Main.cpp

#include "TemplHeader.h"
extern template class foo<int>();
int main() {
    foo<int> test;
    return 0;
}

Je sais qu'il est bon de mettre tout cela dans un seul fichier d'en-tête, mais si nous instancions des modèles avec les mêmes paramètres dans plusieurs fichiers, nous aurions plusieurs définitions identiques et le compilateur les supprimerait toutes (sauf une) pour éviter les erreurs. Comment utiliser extern template? Pouvons-nous l'utiliser uniquement pour les classes, ou pour les fonctions aussi?

De même, les figures 1 et 2 peuvent être étendues à une solution où les modèles sont dans un fichier d’en-tête unique. Dans ce cas, nous devons utiliser le extern template mot-clé pour éviter plusieurs instants identiques. Est-ce seulement pour les classes ou les fonctions aussi?

101
codekiddy

Vous ne devriez utiliser que extern template Pour forcer le compilateur à not instancier un modèle lorsque vous savez qu'il sera instancié ailleurs. Il est utilisé pour réduire le temps de compilation et la taille du fichier objet.

Par exemple:

// header.h

template<typename T>
void ReallyBigFunction()
{
    // Body
}

// source1.cpp

#include "header.h"
void something1()
{
    ReallyBigFunction<int>();
}

// source2.cpp

#include "header.h"
void something2()
{
    ReallyBigFunction<int>();
}

Cela se traduira par les fichiers d'objet suivants:

source1.o
    void something1()
    void ReallyBigFunction<int>()    // Compiled first time

source2.o
    void something2()
    void ReallyBigFunction<int>()    // Compiled second time

Si les deux fichiers sont liés ensemble, une void ReallyBigFunction<int>() sera supprimée, ce qui entraînera une perte de temps de compilation et de taille du fichier objet.

Pour ne pas perdre de temps de compilation et de taille de fichier objet, il existe un mot clé extern qui empêche le compilateur de compiler une fonction modèle. Vous devriez utiliser ceci si et seulement si vous savez il est utilisé dans le même fichier binaire ailleurs.

Changer source2.cpp En:

// source2.cpp

#include "header.h"
extern template void ReallyBigFunction<int>();
void something2()
{
    ReallyBigFunction<int>();
}

Il en résultera les fichiers d'objet suivants:

source1.o
    void something1()
    void ReallyBigFunction<int>() // compiled just one time

source2.o
    void something2()
    // No ReallyBigFunction<int> here because of the extern

Lorsque les deux seront liés, le second fichier objet utilisera simplement le symbole du premier fichier objet. Pas besoin de rejeter ni de perdre du temps de compilation et de la taille du fichier objet.

Cela ne devrait être utilisé que dans un projet, comme lorsque vous utilisez plusieurs fois un modèle tel que vector<int>, Vous devez utiliser extern dans tous les fichiers source sauf un.

Ceci s’applique également aux classes et fonctionne comme une seule et même fonction de membre de modèle.

160
Dani

Wikipedia a le meilleure description

En C++ 03, le compilateur doit instancier un modèle lorsqu'un modèle entièrement spécifié est rencontré dans une unité de traduction. Si le modèle est instancié avec les mêmes types dans plusieurs unités de traduction, cela peut considérablement augmenter les temps de compilation. Il n'y a aucun moyen d'empêcher cela dans C++ 03, c'est pourquoi C++ 11 a introduit les déclarations de modèle externe, analogues aux déclarations de données externes.

C++ 03 a cette syntaxe pour obliger le compilateur à instancier un modèle:

  template class std::vector<MyClass>;

C++ 11 fournit maintenant cette syntaxe:

  extern template class std::vector<MyClass>;

qui indique au compilateur de ne pas instancier le modèle dans cette unité de traduction.

L'avertissement: nonstandard extension used...

Microsoft VC++ avait auparavant une version non standard de cette fonctionnalité pendant quelques années déjà (en C++ 03). Le compilateur met en garde à ce sujet pour éviter les problèmes de portabilité avec le code devant également être compilé sur différents compilateurs.

Regardez l'exemple dans page liée pour voir qu'il fonctionne à peu près de la même façon. Vous pouvez vous attendre à ce que le message disparaisse avec les versions futures de MSVC, sauf bien sûr lorsque vous utilisez les extensions autre du compilateur non standard en même temps.

43
sehe

Le problème connu avec les modèles est le ballonnement de code, conséquence de la génération de la définition de classe dans chaque module qui appelle la spécialisation de modèle de classe. Pour éviter cela, à partir de C++ 0x, on pourrait utiliser le mot clé externe devant la spécialisation de modèle de classe

#include <MyClass> extern template class CMyClass<int>;

L'instantané explicite de la classe de modèle ne devrait se produire que dans une seule unité de traduction, de préférence celle avec la définition de modèle (MyClass.cpp).

template class CMyClass<int>;
template class CMyClass<float>;
4
damirlj

Si vous avez déjà utilisé extern pour des fonctions, la même philosophie est suivie pour les modèles. sinon, aller si extern pour des fonctions simples peut aider. En outre, vous pouvez également placer le ou les extern (s) dans un fichier d’en-tête et inclure l’en-tête lorsque vous en avez besoin.

0
qqqqq