Явная и неявная инстанциация шаблона



Как только компилятор видит полное определение шаблона, он может инстанцировать его с каким-то конкретным аргументом. И тут есть 2 варианта.



Вернемся к вчерашнему примеру с шаблоном корабля.





// ship.hpp



template<typename T>

struct Ship

{

// contain some fields

void TurnShip(T command) {// do some stuff}

};



// main.cpp



#include "ship.hpp"



int main() {

Ship<int> ship;

ship.TurnShip(5);

}





Согласно заветам предыдущего поста, мы перенесли все определение шаблона в хэдэр, подключили этот хэдэр в мэйн и использовали объект. И раз в мэйне мы используем объект, то в этом конкретном случае произошло неявное инстанцирование шаблона - компилятор все сделал за нас. Мы дали ему определение шаблона, укропу, кошачью жопу, ... и охапку дров, а он нам выдал плов. Точнее конкретный код, соответствующий конкретной специализации Ship<int>.



Но вы уже поняли, да? Раз есть неявное инстанцирование, то есть и явное! То есть, мы сами своими ручками-закарючками(осуждаю боди-шейминг, говорю про себя) можем сказать компилятору, что мы хотим, чтобы он инстанцировал нужную нам специализации и сгенерировал нам для нее код. И эта штука поможет нам решить проблему с определением шаблонов в цппшниках.



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





// ship.hpp

template<typename T>

struct Ship

{

// contain some fields

void TurnShip(T command);

};



// ship.cpp

#include "ship.hpp"

#include <string>



template <class T>

void Ship<T>::TurnShip(T command) {/* do stuff using command */}



template struct Ship<std::string>; // HERE IT IS!!



// main.cpp

#include "ship.hpp"

#include <string>



int main() {

Ship<std::string> ship;

ship.TurnShip(std::string{"Turn upside down"});

}





template class Ship<std::string> - яное инстанцирование шаблона. Синтаксис следующий:



template class-key template-name <argument-list>;



class-key - любое из struct/class/union, должно соответствовать оному в самом шаблоне.



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



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



Но в целом, это хорошая практика. Поэтому используйте на здоровье.



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



Find a way out of your problems. Stay cool.



#cppcore #template