Шаблоны С++ объявляют в .h, определяют в .hpp
Я увидел некоторый код, в котором разработчик определил шаблон класса в файле .h и определил его методы в файле .hpp. Это застало меня врасплох.
Существуют ли особые соглашения в С++ при работе с шаблонами и в каких файлах они должны быть?
Например, у меня был шаблон класса Vector
с методами для векторных операций (добавление, вычитание, точка и т.д.). Я также хотел бы специализировать определенные функции, если аргумент шаблона является float
(операторы сравнения). Как бы вы отделили все это между файлами (укажите ли .h,.hpp,.cpp).
Ответы
Ответ 1
Обычно (по моему опыту, YMMV) файл hpp
является #include
-ed CPP файлом. Это делается для того, чтобы разбить код на два физических файла, основной и файл данных реализации, о которых не нужно знать пользователям вашей библиотеки. Это делается следующим образом:
super_lib.h(единственный файл, которому ваши клиенты должны #include
)
template<...> class MyGizmo
{
public:
void my_fancy_function();
};
#include "super_lib_implementation.hpp"
super_lib_implementation.hpp(ваши клиенты не напрямую #include
)
template<...> void MyGizmo<...>::my_fancy_function()
{
// magic happens
}
Ответ 2
Это звучит необычно для меня. Определение шаблона и все специализации должны быть скомпилированы вместе с его объявлением, за исключением export
templates, функции, которая фактически не существует.
С++ 0x вводит объявления extern
шаблонов, которые позволяют вам определять явные специализации в другом исходном файле (единица перевода). Это уже существует как расширение в GCC и, возможно, других платформах.
Разделение на два файла может помочь "скрыть" реализацию немного, или, возможно, дать некоторую лень с помощью doxygen.
Ага! Это также возможно улучшить время компиляции с предварительно скомпилированными заголовками. Компилятор может кэшировать заголовки для каждого файла. Затем может быть изменен отдельный заголовок "реализация", не касаясь заголовка "interface". Но не наоборот, заголовок реализации по-прежнему будет занимать большую часть времени компиляции, а коэффициент усиления будет очень хрупким и зависит от платформы и конкретных изменений. В итоге PCH улучшает время над несколькими исходными файлами, а оптимизация зависимостей заголовков бессмысленна.
Ответ 3
Мне кажется, что это путаный способ разделения кода. .h
обозначает заголовок и .hpp
для заголовка С++. Помещение определений шаблонов в .hpp
, в то время как другой код в .h
, похоже, нарушает расширение файла.
Обычно код шаблона записывается в один заголовок вместе с объявлением шаблона или в другой заголовок, который также может быть специально зафиксифицирован как .tcc
или что-то еще, а затем включен в заголовок, в который помещаются объявления шаблонов., расширение файла не имеет значения, если вы согласны в своем проекте.
Исключения - это когда вы используете явное создание экземпляра и точно знаете, какие экземпляры вам понадобятся. Представьте, что у вас есть шаблон и ровно два его экземпляра:
template<typename T>
struct SymbolTable {
T *lookup();
// ...
};
template struct SymbolTable<GlobalSym>;
template struct SymbolTable<LocalSym>;
Вам не нужно вставлять определение lookup
и других в заголовок. Вы можете поместить их в файл .cpp
вместе с двумя явными директивами создания экземпляров.
Последний вопрос, о котором вы спрашиваете, касается другой темы: Явные специализации. Я рекомендую вам задать отдельный вопрос. Вкратце, явные определения специализации, в которых все аргументы шаблона имеют конкретные значения/типы, должны быть помещены в файл .cpp
, но их объявления должны быть помещены в заголовок (чтобы сообщить другим, что эти определенные члены являются специализированными).
Ответ 4
Я никогда не слышал о объявлении классов в .h
и определениях шаблонов в .hpp
. Каждый проект, который я видел, использовал подход, который .h
и .hpp
означает одно и то же, и вы должны стандартизировать один (обычно .h
).
Методы шаблонов можно поместить в конец файла .h
, или их можно поместить в отдельные файлы -inl.h
(как предложено Руководство по стилю Google С++, например).
Ответ 5
Я думаю, что один из аспектов разделения файла интерфейса (.h) и реализации (.hpp) заключается в том, что пользователю шаблона (то есть другому разработчику) нужно будет только посмотреть файл .h, чтобы понять, как использовать шаблон, не отвлекаясь на его фактическую реализацию.
Ответ 6
Если мы определяем шаблон класса в файле .h и определяем его методы в файле .hpp, мы должны #include
.hpp файл в файле .h. Легче просто определить методы в конце файла .h(тогда не требуется файл .hpp).
Ответ 7
Библиотека Boost использует ".hpp" для заголовков, содержащих декларацию и реализацию. В таких случаях нет необходимости связываться с (предварительно скомпилированной) библиотекой.
В отношении идеи Boost такое расширение, как ".inl", может быть хорошей идеей описать исходный файл "реализации" заголовка объявления шаблона.