Ответ
Шаблоны в C++ можно писать в различных местах, но основное правило связано с моделью компиляции: компилятору нужен полный код шаблона в момент его инстанцирования (создания конкретной версии для типа).
Основные места размещения:
-
Заголовочные файлы (.hpp/.h): Наиболее распространенный и удобный способ. Весь код шаблона (объявление и определение) находится в заголовке.
// vector_utils.hpp #pragma once template <typename T> class SimpleVector { private: T* data_; size_t size_; public: explicit SimpleVector(size_t size) : size_(size), data_(new T[size]) {} ~SimpleVector() { delete[] data_; } T& operator[](size_t index) { return data_[index]; } }; template <typename T> T sum(const SimpleVector<T>& vec) { /* реализация здесь */ } -
Явное инстанцирование в .cpp файле: Можно вынести определение шаблона в .cpp файл, но тогда необходимо явно инстанцировать его для всех используемых типов. Это сокращает время компиляции.
// my_template.cpp template <typename T> T max(T a, T b) { return (a > b) ? a : b; } // Явное инстанцирование для int и double template int max<int>(int, int); template double max<double>(double, double);В заголовке останется только объявление:
template <typename T> T max(T a, T b);. -
Внутри классов/структур: Шаблонные методы можно определять прямо внутри класса.
template <typename Key, typename Value> class Cache { public: // Шаблонный метод внутри шаблонного класса template <typename InputIt> void insertRange(InputIt first, InputIt last) { for (; first != last; ++first) { storage_.insert(*first); } } private: std::map<Key, Value> storage_; };
Рекомендация: Для большинства проектов полное определение шаблонов в заголовочных файлах — это стандартный и наименее проблемный подход.
Ответ 18+ 🔞
Ёпта, ну смотри, шаблоны в C++ — это как хитрая жопа, которую можно запихнуть в разные места, но главное правило — компилятору нужен весь код шаблона, когда он начинает из него конкретную хуйню для твоего типа делать. Это называется инстанцированием, если ты вдруг забыл.
Куда их, блядь, можно пихать:
-
В заголовочные файлы (.hpp/.h): Самый, блядь, популярный и простой способ. Всю подноготную шаблона — и объявление, и определение — суёшь прямо в хедер. Компилятор потом везде, где этот хедер инклюдят, видит всё целиком и спокоен, как удав.
// vector_utils.hpp #pragma once template <typename T> class SimpleVector { private: T* data_; size_t size_; public: explicit SimpleVector(size_t size) : size_(size), data_(new T[size]) {} ~SimpleVector() { delete[] data_; } T& operator[](size_t index) { return data_[index]; } }; template <typename T> T sum(const SimpleVector<T>& vec) { /* реализация здесь */ } -
Явное инстанцирование в .cpp файле: Вот это уже для тех, кто хочет скомпилироваться побыстрее. Ты выносишь реализацию шаблона в .cpp, но тогда должен прямо там, в этом же файле, перечислить все типы, для которых ты его будешь использовать. А в заголовке оставляешь только обещание, что такая функция будет. Если забудешь тип указать — получишь ошибку линковки, и будешь потом ебать мои старые костыли, пока ищешь, где же твой
maxдляfloatпотерялся.// my_template.cpp template <typename T> T max(T a, T b) { return (a > b) ? a : b; } // Явное инстанцирование для int и double template int max<int>(int, int); template double max<double>(double, double);В заголовке останется только строчка:
template <typename T> T max(T a, T b);. Доверия к такому подходу, блядь, ноль, пока сам всё не проверишь. -
Прямо внутри классов: Ну это вообще ебушки-воробушки. Шаблонный метод внутри шаблонного же класса. Получается матрёшка, пиздец. Но иногда удобно.
template <typename Key, typename Value> class Cache { public: // Шаблонный метод внутри шаблонного класса template <typename InputIt> void insertRange(InputIt first, InputIt last) { for (; first != last; ++first) { storage_.insert(*first); } } private: std::map<Key, Value> storage_; };
Вывод, ёпта: Если не хочешь себе геморроя и удивления на уровне «ни хуя себе, почему не линкуется», просто пихай все шаблоны целиком в заголовки. Это стандартно, предсказуемо и мозг не ебёт.