использование внешнего шаблона (C ++ 11) - PullRequest
98 голосов
/ 15 ноября 2011

Рисунок 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, чтобы избежать нескольких одинаковых экземпляров.Это только для классов или функций тоже?

Ответы [ 4 ]

159 голосов
/ 15 ноября 2011

Вы должны использовать extern template только для того, чтобы компилятор не создавал шаблон, когда вы знаете , что он будет создан где-то еще.Он используется для уменьшения времени компиляции и размера объектного файла.

Например:

// 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>();
}

Это приведет к созданию следующих объектных файлов:

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

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

Если обафайлы связаны друг с другом, один void ReallyBigFunction<int>() будет отброшен, что приведет к потере времени компиляции и размера файла объекта.

Чтобы не тратить время компиляции и размер файла объекта, есть ключевое слово extern, которое делает компиляторне скомпилировать шаблонную функцию.Вам следует использовать этот тогда и только тогда, когда вы знаете, , что он используется в том же двоичном файле где-то еще.

Изменение source2.cpp на:

// source2.cpp

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

приведет кследующие объектные файлы:

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

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

Когда оба они будут связаны друг с другом, второй объектный файл будет просто использовать символ из первого объектного файла.Нет необходимости отбрасывать, не тратить время на компиляцию и размер объектного файла.

Это следует использовать только внутри проекта, например, когда вы используете шаблон, например vector<int> несколько раз, вы должны использовать externво всех файлах, кроме одного.

Это также относится к классам и функциям как единым, и даже к функциям-членам шаблона.

43 голосов
/ 15 ноября 2011

Википедия имеет лучшее описание

В C ++ 03 компилятор должен создавать экземпляр шаблона всякий раз, когда в модуле перевода встречается полностью указанный шаблон.Если шаблон создается с одними и теми же типами во многих единицах перевода, это может значительно увеличить время компиляции.В C ++ 03 нет способа предотвратить это, поэтому в C ++ 11 введены объявления шаблонов extern, аналогичные объявлениям extern данных.

C ++ 03 имеет этот синтаксис, чтобы обязать компилятор создавать экземпляр шаблона:

  template class std::vector<MyClass>;

C ++ 11 теперь предоставляет следующий синтаксис:

  extern template class std::vector<MyClass>;

, который говорит компилятору не создавать экземпляр шаблона в этом модуле перевода.

Предупреждение: nonstandard extension used...

Microsoft VC ++ раньше имел нестандартную версию этой функции уже несколько лет (на С ++ 03).Об этом предупреждает компилятор, чтобы предотвратить проблемы переносимости с кодом, который также необходим для компиляции на разных компиляторах.

Посмотрите на пример на связанной странице , чтобы увидеть, чтоэто работает примерно так же.Вы можете ожидать, что сообщение исчезнет с будущими версиями MSVC, за исключением, конечно, одновременного использования других нестандартных расширений компилятора.

4 голосов
/ 23 октября 2016

Известной проблемой с шаблонами является раздувание кода, которое является следствием генерации определения класса в каждом модуле, который вызывает специализацию шаблона класса.Чтобы предотвратить это, начиная с C ++ 0x, можно использовать ключевое слово extern перед специализацией шаблона класса

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

Явное создание экземпляра класса шаблона должно происходить только в одной единице перевода, предпочтительно с определением шаблона (MyClass.cpp)

template class CMyClass<int>;
template class CMyClass<float>;
0 голосов
/ 20 января 2015

Если вы ранее использовали extern для функций, то же самое относится и к шаблонам.в противном случае может помочь extern для простых функций.Кроме того, вы можете поместить extern (ы) в заголовочный файл и включать заголовок, когда вам это нужно.

...