Рисунок 1: шаблоны функций
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;
}
Это правильный способ использования extern template
, или я использую это ключевое слово только для шаблонов классов, как на рисунке 2?
Рисунок 2: шаблоны классов
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;
}
Я знаю, что хорошо помещать все это в один файл заголовка, но если мы создадим экземпляры шаблонов с одинаковыми параметрами в нескольких файлах, то мы получим несколько одинаковых определений, и компилятор удалит их все (кроме одного), чтобы избежать ошибок. Как пользоваться extern template
? Можем ли мы использовать его только для классов или мы можем использовать его и для функций?
Кроме того, рисунки 1 и 2 могут быть расширены до решения, в котором шаблоны находятся в одном файле заголовка. В этом случае нам нужно использовать extern template
ключевое слово, чтобы избежать нескольких одинаковых мгновений. Это тоже только для классов или функций?
extern template class foo<int>();
похоже на ошибку.()
с внешней строкой написано «ожидаемый неквалифицированный идентификатор» . и ваша книга, и визуальная студия ошибаются, попробуйте использовать более стандартный компилятор, такой как g ++ или clang, и вы увидите проблему.Ответы:
Вы должны использовать только
extern template
для того, чтобы заставить компилятор не создавать экземпляр шаблона, когда вы знаете, что он будет создан где-то еще. Он используется для уменьшения времени компиляции и размера объектного файла.Например:
В результате получатся следующие объектные файлы:
Если оба файла связаны вместе, один
void ReallyBigFunction<int>()
будет отброшен, что приведет к потере времени компиляции и размеру объектного файла.Чтобы не тратить время компиляции и размер объектного файла, есть
extern
ключевое слово, которое запрещает компилятору компилировать функцию шаблона. Вы должны использовать это тогда и только тогда, когда знаете, что он используется в том же двоичном файле где-то еще.Переход
source2.cpp
на:В результате получатся следующие объектные файлы:
Когда оба они будут связаны вместе, второй объектный файл будет просто использовать символ из первого объектного файла. Нет необходимости отбрасывать, не тратить впустую время компиляции и размер объектного файла.
Это следует использовать только внутри проекта, например, когда вы используете шаблон
vector<int>
несколько раз, вы должны использовать егоextern
во всех исходных файлах, кроме одного.Это также относится к классам и функциям как к одному, и даже к функциям-членам шаблона.
источник
В Википедии есть лучшее описание
Предупреждение:
nonstandard extension used...
В Microsoft VC ++ уже несколько лет была нестандартная версия этой функции (в C ++ 03). Компилятор предупреждает об этом, чтобы предотвратить проблемы с переносимостью кода, который также необходимо компилировать на разных компиляторах.
Посмотрите на образец на связанной странице, чтобы увидеть, что он работает примерно так же. Вы можете ожидать, что сообщение исчезнет в будущих версиях MSVC, за исключением, конечно, одновременного использования других нестандартных расширений компилятора.
источник
std::vector
(почти наверняка все они), неextern
имеет никакого эффекта.extern template
требуется только в том случае, если объявление шаблона завершеноНа это намекали в других ответах, но я не думаю, что этому уделялось достаточно внимания.
Это означает, что в примерах OPs это не
extern template
имеет никакого эффекта, потому что определения шаблонов в заголовках были неполными:void f();
: просто декларация, без телаclass foo
: объявляет метод,f()
но не имеет определенияПоэтому я бы рекомендовал просто удалить
extern template
определение в этом конкретном случае: вам нужно только добавить их, если классы полностью определены.Например:
TemplHeader.h
TemplCpp.cpp
main.cpp
компилировать и просматривать символы с помощью
nm
:вывод:
а затем, как
man nm
мы видим, этоU
означает undefined, поэтому определение осталось только поTemplCpp
желанию.Все это сводится к компромиссу между полными объявлениями заголовков:
extern template
для каждого включающего, что программисты, вероятно, забудут сделатьДальнейшие их примеры показаны по адресу: Явное создание шаблона - когда оно используется?
Поскольку время компиляции очень важно в больших проектах, я настоятельно рекомендую использовать неполные объявления шаблонов, если только сторонним сторонам не потребуется повторно использовать ваш код со своими собственными сложными пользовательскими классами.
И в этом случае я бы сначала попытался использовать полиморфизм, чтобы избежать проблемы времени сборки, и использовать шаблоны только в том случае, если можно добиться заметного увеличения производительности.
Протестировано в Ubuntu 18.04.
источник
Известная проблема с шаблонами - раздувание кода, которое является следствием генерации определения класса в каждом модуле, который вызывает специализацию шаблона класса. Чтобы предотвратить это, начиная с C ++ 0x, можно использовать ключевое слово extern перед специализацией шаблона класса.
Явное создание экземпляра класса шаблона должно происходить только в одной единице трансляции, предпочтительно в той, которая имеет определение шаблона (MyClass.cpp).
источник
Если вы раньше использовали extern для функций, точно такая же философия применяется к шаблонам. в противном случае может помочь использование extern для простых функций. Кроме того, вы можете поместить extern (s) в файл заголовка и включить заголовок, когда он вам нужен.
источник