Когда НЕ стоит использовать extern template



Если вы будете гуглить инфу по этой теме, то непременно нарветесь на неправильное понимание принципов работы фичи. Стопроцентов вы наткнетесь на такое объяснение:



extern template в связке с явным инстанцированием шаблона помогает предотвратить дублирование кода в TU и уменьшить время компиляции. Выглядит это так:



// ship.hpp

#pragma once

template<typename T>

struct Ship

{

void TurnShip(T command);

};



// ship.cpp

#include "ship.hpp"



template <class T>

void Ship<T>::TurnShip(T command) {}



template struct Ship<int>; // explicit instantiation definition



// main.cpp

#include "ship.hpp"



extern template struct Ship<int>; // explicit instantiation declaration



int main() {

Ship<int> ship;

ship.TurnShip(5);

}



Типа вот мы в ship.cpp добавили явную конкретизацию шаблона, а в мэйне объявили, что возьмем информацию о специализации в другом месте.



Но дело в том, что в этом случае extern template - лишний! В нем нет никакого смысла и вот почему.



Если мы уберем extern template из файла main, то ничего не изменится. Так как в этой единице трансляции и так никогда бы не была конкретизирована специализация Ship<int>. Потому что компилятору на момент компиляции файла main.cpp видно только объявление шаблона из файла ship.hpp и у него недостаточно информации для инстанцирования. И только при линковке линковщик найдет все символы в единице трансляции, соответствующей ship.cpp, и сгенерирует рабочую программу.



Так что запомните: если вы используете явную инстанциацию после определения шаблона в цппшнике и подключаете хэдэр с его объявлением, то вам НЕ НУЖНО использовать extern template.



Это кстати отличная защита от тех самых проблем при работе с шаблонами. Так что выносить определенения шаблонов в цппшники и делать в них явную инстанциацию - полезная вещь.



Также без подключения хэдэра эта вещь вообще не работает, в отличии например от глобальных переменных. Все-таки контекст extern здесь не совсем совпадает.



А вот когда это нужно использовать. Только тогда, когда у вас есть несколько единиц трансляции, где компилятор сам неявно может инстанцировать одинаковые специализации. Например, когда вы полностью определяете шаблон в хэдэре и везде его распространяете таким образом. Тогда получается, что без использования extern template в каждой из этих единиц трансляций, подключивших хэдэр с шаблоном и использующих одинаковую специализацию, эта специализация будет инстанцирована. Это значит, что код для нее будет присутствовать во всех объектниках. Это приводит к его дублированию и увеличению времени компиляции.



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



Rely on original information. Stay cool.



#cpp11 #cppcore #template #compiler