Rysunek 1: szablony funkcji
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;
}
Czy jest to właściwy sposób użycia extern template
, czy też używam tego słowa kluczowego tylko dla szablonów klas, jak na rysunku 2?
Rysunek 2: szablony klas
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;
}
Wiem, że dobrze jest umieścić to wszystko w jednym pliku nagłówkowym, ale jeśli utworzymy wystąpienia szablonów z tymi samymi parametrami w wielu plikach, otrzymamy wiele takich samych definicji i kompilator usunie je wszystkie (z wyjątkiem jednego), aby uniknąć błędów. Jak używać extern template
? Czy możemy go używać tylko do klas, czy też możemy go używać do funkcji?
Również Rysunek 1 i Rysunek 2 można rozszerzyć do rozwiązania, w którym szablony znajdują się w jednym pliku nagłówkowym. W takim przypadku musimy użyć extern template
słowa kluczowego, aby uniknąć wielu takich samych instancji. Czy dotyczy to tylko klas lub funkcji?
extern template class foo<int>();
Wydaje się też, że to pomyłka.()
linii zewnętrznej jest napisane „oczekiwany niekwalifikowany identyfikator” . zarówno twoja książka, jak i studio wizualne są złe, spróbuj użyć bardziej zgodnego ze standardami kompilatora, takiego jak g ++ lub clang, a zobaczysz problem.Odpowiedzi:
Powinieneś używać tylko
extern template
do wymuszenia na kompilatorze, aby nie tworzył instancji szablonu, gdy wiesz , że zostanie on utworzony w innym miejscu. Służy do skrócenia czasu kompilacji i rozmiaru pliku obiektowego.Na przykład:
Spowoduje to powstanie następujących plików obiektów:
Jeśli oba pliki są ze sobą połączone, jeden z nich
void ReallyBigFunction<int>()
zostanie odrzucony, co spowoduje marnowanie czasu kompilacji i rozmiaru pliku obiektowego.Aby nie marnować czasu kompilacji i rozmiaru pliku obiektowego, istnieje
extern
słowo kluczowe, które powoduje, że kompilator nie kompiluje funkcji szablonu. Powinieneś użyć tego wtedy i tylko wtedy, gdy wiesz, że jest używany w tym samym pliku binarnym gdzie indziej.Zmiana
source2.cpp
na:W rezultacie powstają następujące pliki obiektów:
Kiedy oba zostaną połączone ze sobą, drugi plik obiektowy użyje tylko symbolu z pierwszego pliku obiektowego. Nie ma potrzeby usuwania i marnowania czasu kompilacji i rozmiaru pliku obiektowego.
Powinno to być używane tylko w projekcie, na przykład w sytuacjach, gdy używasz szablonu
vector<int>
wiele razy, powinieneś używaćextern
we wszystkich plikach źródłowych z wyjątkiem jednego.Dotyczy to również klas i funkcji jako jednej, a nawet funkcji składowych szablonu.
źródło
Wikipedia ma najlepszy opis
Ostrzeżenie:
nonstandard extension used...
Microsoft VC ++ już od kilku lat miał niestandardową wersję tej funkcji (w C ++ 03). Kompilator ostrzega o tym, aby zapobiec problemom z przenośnością kodu, który musiał być kompilowany również na różnych kompilatorach.
Spójrz na próbkę na połączonej stronie, aby zobaczyć, że działa mniej więcej w ten sam sposób. Możesz oczekiwać, że komunikat zniknie wraz z przyszłymi wersjami MSVC, z wyjątkiem oczywiście sytuacji, gdy w tym samym czasie używasz innych niestandardowych rozszerzeń kompilatora.
źródło
std::vector
(prawie na pewno wszystkie),extern
nie ma to żadnego efektu.extern template
jest potrzebny tylko wtedy, gdy deklaracja szablonu jest kompletnaWskazywano na to w innych odpowiedziach, ale nie sądzę, aby położono na to wystarczający nacisk.
Oznacza to, że w przykładach PO
extern template
nie ma to znaczenia, ponieważ definicje szablonów w nagłówkach były niekompletne:void f();
: tylko deklaracja, brak treściclass foo
: deklaruje metodę,f()
ale nie ma definicjiDlatego zalecałbym po prostu usunięcie
extern template
definicji w tym konkretnym przypadku: wystarczy je dodać tylko wtedy, gdy klasy są całkowicie zdefiniowane.Na przykład:
TemplHeader.h
TemplCpp.cpp
Main.cpp
kompiluj i przeglądaj symbole za pomocą
nm
:wynik:
a potem,
man nm
jak widzimy,U
oznacza to niezdefiniowane, więc definicja pozostawała włączona tylkoTemplCpp
zgodnie z życzeniem.Wszystko to sprowadza się do kompromisu pełnych deklaracji nagłówka:
extern template
do każdego elementu włączającego, o którym programiści prawdopodobnie zapomnąDalsze przykłady można znaleźć pod adresem: Jawna instancja szablonu - kiedy jest używana?
Ponieważ czas kompilacji jest tak krytyczny w dużych projektach, zdecydowanie polecam niekompletne deklaracje szablonów, chyba że strony zewnętrzne absolutnie muszą ponownie użyć twojego kodu z własnymi złożonymi klasami niestandardowymi.
W takim przypadku najpierw spróbuję użyć polimorfizmu, aby uniknąć problemu z czasem kompilacji, i używać szablonów tylko wtedy, gdy można uzyskać zauważalny wzrost wydajności.
Testowane w Ubuntu 18.04.
źródło
Znanym problemem związanym z szablonami jest rozdęcie kodu, które jest konsekwencją generowania definicji klasy w każdym module wywołującym specjalizację szablonu klas. Aby temu zapobiec, zaczynając od C ++ 0x, można użyć słowa kluczowego extern przed specjalizacją szablonu klasy
Jawna instancja klasy szablonu powinna mieć miejsce tylko w jednej jednostce tłumaczeniowej, najlepiej tej z definicją szablonu (MyClass.cpp)
źródło
Jeśli wcześniej używałeś extern dla funkcji, dokładnie ta sama filozofia jest stosowana w przypadku szablonów. jeśli nie, pomocne może być skorzystanie z programu zewnętrznego w celu uzyskania prostych funkcji. Możesz także chcieć umieścić extern (y) w pliku nagłówkowym i dołączyć nagłówek, kiedy go potrzebujesz.
źródło