Przechowywanie definicji funkcji szablonów C++ w a.CPP plik

Mam kod szablonu, który wolałbym przechowywać w pliku CPP zamiast w nagłówku. Wiem, że można to zrobić tak długo, jak wiesz, które typy szablonów będą używane. Na przykład:

.plik h

class foo
{
public:
    template <typename T>
    void do(const T& t);
};

.plik cpp

template <typename T>
void foo::do(const T& t)
{
    // Do something with t
}

template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);

Zwróć uwagę na dwie ostatnie linie - funkcja szablonu foo::do jest używana tylko z ciągami ints i std::, więc te definicje oznaczają, że aplikacja się połączy.

Moje pytanie brzmi - czy to jest paskudny hack czy will to działa z innymi kompilatorami/linkerami? Używam tylko tego kodu z VS2008 w tej chwili, ale będę chciał portować do innych środowisk.

 385
Author: Leon Timmermans, 2008-09-22

11 answers

Opisywany problem można rozwiązać definiując szablon w nagłówku lub za pomocą podejścia opisanego powyżej.

Polecam przeczytać następujące punkty z C++ FAQ Lite :

Wnikają w wiele szczegółów na temat tych (i innych) problemów z szablonami.

 171
Author: Aaron N. Tubbs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-22 01:12:42

Dla innych na tej stronie zastanawiających się, jaka jest poprawna składnia (tak jak i) dla jawnej specjalizacji szablonów (lub przynajmniej w VS2008), jest to następujące...

W Twoim .plik H...
template<typename T>
class foo
{
public:
    void bar(const T &t);
};
I w Twoim .plik cpp
template <class T>
void foo<T>::bar(const T &t)
{ }

// Explicit template instantiation
template class foo<int>;
 90
Author: namespace sid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-10 02:15:08

Ten kod jest dobrze uformowany. Musisz tylko zwrócić uwagę, że definicja szablonu jest widoczna w momencie tworzenia instancji. Cytując standard, § 14.7.2.4:

Definicja nieweksportowanego szablonu funkcji, nieweksportowanego szablonu funkcji członka lub nieweksportowanej funkcji członka lub elementu danych statycznych szablonu klasy powinna być obecna w każdej jednostce translacyjnej, w której jest bezpośrednio utworzona instancja.

 16
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 16:15:32

Powinno to działać poprawnie wszędzie tam, gdzie obsługiwane są szablony. Explicit template instantiation jest częścią standardu C++.

 10
Author: moonshadow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 16:58:14

Twój przykład jest poprawny, ale niezbyt przenośny. Istnieje również nieco czystsza składnia, która może być użyta (jak wskazuje @namespace-sid).

Załóżmy, że Klasa szablonowa jest częścią jakiejś biblioteki, która ma być udostępniona. Czy inne wersje klasy template powinny być kompilowane? Czy opiekun biblioteki powinien przewidywać wszystkie możliwe zastosowania klasy w szablonach?

Alternatywne podejście jest lekką odmianą tego, co masz: dodaj trzeci plik, który jest szablonem plik implementacji / instancji.

Foo.plik h

// Standard header file guards omitted

template <typename T>
class foo
{
public:
    void bar(const T& t);
};

Foo.plik cpp

// Always include your headers
#include "foo.h"

template <typename T>
void foo::bar(const T& t)
{
    // Do something with t
}

Foo-impl.plik cpp

// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;

Jedynym zastrzeżeniem jest to, że musisz powiedzieć kompilatorowi, aby skompilował foo-impl.cpp zamiast foo.cpp, ponieważ kompilacja tego ostatniego nic nie robi.

Oczywiście możesz mieć wiele implementacji w trzecim pliku lub mieć wiele implementacji dla każdego typu, którego chcesz użyć.

Umożliwia to znacznie większą elastyczność podczas udostępniania klasy szablonowej do innych zastosowań.

Ta konfiguracja skraca również czas kompilacji dla klas używanych ponownie, ponieważ nie rekompilujesz tego samego pliku nagłówkowego w każdej jednostce tłumaczenia.

 6
Author: Cameron Tacklind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-06 05:15:23

To na pewno nie jest paskudny hack, ale pamiętaj, że będziesz musiał to zrobić (wyraźna specjalizacja szablonu) dla każdej klasy/typu, którego chcesz użyć z danym szablonem. W przypadku wielu typów żądających instancji szablonu może być wiele linii w Twoim .plik cpp. Aby rozwiązać ten problem, możesz mieć TemplateClassInst.cpp w każdym projekcie, którego używasz, dzięki czemu masz większą kontrolę nad tym, jakie typy zostaną utworzone. Oczywiście to rozwiązanie nie będzie idealne (aka silver bullet) jak można skończyć łamanie ODR :).

 5
Author: Red XIII,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-06 21:44:49

Istnieje, w najnowszym standardzie, słowo kluczowe (export), które pomogłoby złagodzić ten problem, ale nie jest zaimplementowane w żadnym znanym mi kompilatorze, innym niż Comeau.

Zobacz FAQ-lite na ten temat.

 4
Author: Ben Collins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 16:07:57

Tak, to standardowy sposób wykonywania specializiation explicit instantiation. Jak już stwierdziłeś, nie możesz utworzyć instancji tego szablonu z innymi typami.

Edit: poprawione na podstawie komentarza.

 3
Author: Lou Franco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-22 18:47:40

Nie ma nic złego w podanym przez Ciebie przykładzie. Ale muszę powiedzieć, że uważam, że przechowywanie definicji funkcji w pliku cpp nie jest efektywne. Rozumiem tylko potrzebę oddzielenia deklaracji i definicji funkcji.

Używane razem z jawną instancją klasy, Biblioteka Boost Concept Check Library (BCCL) może pomóc w wygenerowaniu kodu funkcji szablonu w plikach cpp.

 0
Author: Benoît,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-27 15:13:07

Jest to standardowy sposób definiowania funkcji szablonu. Myślę, że są trzy metody definiowania szablonów. Albo prawdopodobnie 4. Każdy z plusami i minusami.

  1. Zdefiniuj w definicji klasy. Nie podoba mi się to w ogóle, ponieważ myślę, że definicje klas są wyłącznie w celach informacyjnych i powinny być łatwe do odczytania. Jednak definiowanie szablonów w klasie jest znacznie mniej trudne niż na zewnątrz. I nie wszystkie deklaracje szablonów są na tym samym poziomie złożoności. Metoda ta sprawia również, że szablon prawdziwy szablon.

  2. Zdefiniuj szablon w tym samym nagłówku, ale poza klasą. To jest mój ulubiony sposób przez większość czasu. Utrzymuje porządek w definicji klasy, szablon pozostaje prawdziwym szablonem. Wymaga jednak pełnego nazewnictwa szablonów, co może być trudne. Ponadto Twój kod jest dostępny dla wszystkich. Ale jeśli chcesz, aby Twój kod był wbudowany, jest to jedyny sposób. Można to również osiągnąć, tworząc .Plik INL na końcu klasy definicje.

  3. Dołącz nagłówek.h i wdrożenie.CPP do twojego głównego.CPP. Myślę, że tak to się robi. Nie będziesz musiał przygotowywać żadnych wstępnych instancji, będzie zachowywał się jak prawdziwy szablon. Problem, który mam z tym polega na tym, że nie jest to naturalne. Zwykle nie uwzględniamy i nie spodziewamy się dołączania plików źródłowych. Domyślam się, że skoro włączyłeś plik źródłowy, funkcje szablonu mogą być wbudowane.

  4. Ta ostatnia metoda, którą był sposób posted, definiuje szablony w pliku źródłowym, podobnie jak numer 3; ale zamiast włączać plik źródłowy, wstępnie tworzymy szablony do tych, których będziemy potrzebować. Nie mam problemu z tą metodą i czasem się przydaje. Mamy jeden duży kod, nie może korzystać z inlined więc po prostu umieścić go w pliku CPP. A jeśli znamy typowe instancje i możemy je predefiniować. To oszczędza nam od pisania zasadniczo tej samej rzeczy 5, 10 razy. Ta metoda ma tę zaletę, że nasz kod jest zastrzeżony. Ale Ja nie zaleca się umieszczania małych, regularnie używanych funkcji w plikach CPP. Ponieważ zmniejszy to wydajność twojej Biblioteki.

Uwaga, nie jestem świadomy konsekwencji nadętego pliku obj.

 0
Author: Javene CPP McGowan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-21 18:25:06

Czas na aktualizację! Utwórz inline (.inl, lub prawdopodobnie jakikolwiek inny) plik i po prostu skopiuj w nim wszystkie swoje definicje. Pamiętaj, aby dodać szablon nad każdą funkcją (template <typename T, ...>). Teraz zamiast włączać plik nagłówka do pliku wbudowanego, robisz odwrotnie. Dołącz plik inline Po deklaracji twojej klasy (#include "file.inl").

Nie wiem, dlaczego nikt o tym nie wspomniał. Nie widzę natychmiastowych wad.
 -1
Author: Didii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-22 01:48:26