Резюме
Этот вопрос касается достижения отдельной компиляции одного экземпляра класса шаблона в нескольких различных единицах перевода.
Вопрос
Для нешаблонных классов можно поместить определения в несколько файлов .cpp и скомпилировать их отдельно. Например:
файл A.h:
class A {
public:
void func1();
void func2();
void func3() { /* defined in class declaration */}
}
файл A1.cpp:
void A::func1() { /* do smth */ }
файл A2.cpp:
void A::func2() { /* do smth else */ }
Теперь я попытался сделать что-то похожее с шаблонами классов. Поскольку я точно знаю, какие экземпляры мне понадобятся, я явно создаю экземпляры шаблонов. Я собираю каждый экземпляр отдельно, потому что функции-члены содержат довольно большие математические выражения, которые могут значительно замедлить компилятор на высоких уровнях оптимизации. Поэтому я попробовал следующее:
файл TA.h:
template <typename T>
class TA {
public:
void func1();
void func2();
void func3() { /* defined in class declaration */}
}
файл TA1.cpp:
template <typename T>
void TA<T>::func1() { /* do smth */ }
template class TA<sometype>;
файл TA2.cpp:
template <typename T>
void TA<T>::func2() { /* do smth else */ }
template class TA<sometype>;
Он работает с clang и GCC в Linux, но не работает с GCC на Mac во время компоновки во время ошибки дублирующихся символов (в этом примере из-за func3, который был создан как в TA1.cpp, так и в TA2.cpp).
Затем я наткнулся на это предложение в стандарте:
C ++ 11.14.7, пункт 5:
Для данного шаблона и заданного набора аргументов шаблона,
— явное определение экземпляра должно появляться в программе не более одного раза,
— …
Означает ли это, что отдельная компиляция шаблонных классов невозможна (не разрешена) даже при использовании явной реализации (очевидно, это невозможно при неявной реализации)?
PS Мне все равно, так как я получил свой ответ, но тот, кто думает, что ответили здесь https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file неправильно.
Отдельная компиляция шаблонов сложна, но допустима. То, что вы не можете сделать, это явно создать экземпляр типа в нескольких единицах перевода, так же, как вы не можете определить одну и ту же функцию в двух единицах перевода.
После другого взгляда на стандарт, мне кажется, что единственно разумным вариантом является использование одного явного создания экземпляра класса шаблона в сочетании с явным созданием экземпляров функций-членов небольшого числа «сложных» функций.
Это (согласно 14.7.2p9) будет создавать экземпляр класса и всех членов, которые были определены до этого момента (которые должны включать все Кроме «трудные» участники). Затем эти выбранные члены могут быть явно созданы в других единицах перевода, содержащих их определения.
Это сделало бы мой пример похожим ниже (при условии, что TA1.cpp содержит простые функции и единственную «сложную» функцию в Т.А. является func2)
файл TA1.cpp:
template <typename T>
void TA<T>::func1() { /* "simple" function definition */ }
template class TA<sometype>; /* expl. inst. of class */
файл TA2.cpp:
template <typename T>
void TA<T>::func2() { /* "difficult" function definition */ }
template void TA<sometype>::func2(); /* expl. inst. of member */
Этот метод требует, чтобы мы написали явное определение экземпляра для каждой «сложной» функции, что утомительно, но также заставляет нас дважды подумать, действительно ли мы хотим сохранить это отдельно или нет.
отказ
Когда это может быть полезно? Не часто. Как уже упоминали другие люди, не рекомендуется разбивать определения классов на несколько файлов. В моем конкретном случае «сложные» функции содержат сложные математические операции на экземплярах нетривиальных классов. Шаблоны C ++ не славятся высокой скоростью компиляции, но в этом случае это было невыносимо. Эти функции вызывают друг друга, что отправляет компилятору долгий и трудоемкий путь расширения / встраивания перегруженных операторов / шаблонов / и т. Д. Для оптимизации всего, что он видит, с практически нулевым улучшением, но компиляция длится часами. Эта хитрость выделения некоторых функций в отдельных файлах ускоряет компиляцию в 20 раз (и позволяет также распараллеливать ее).