Подтвердить что ты не робот

Как примирить идиому С++ для разделения заголовка/источника с помощью шаблонов?

Мне интересно немного об этом шаблоном бизнесе.

В C и С++ очень часто ставить объявления в файлах заголовков и определениях в исходных файлах и сохранять их полностью отдельно. Однако это даже не представляется возможным (каким бы то ни было образом), когда дело касается шаблонов, и, как мы все знаем, шаблоны - отличный инструмент.

Кроме того, Boost является главным образом заголовками, так что это реальная проблема. Разделение заголовков и источника по-прежнему является хорошей идеей в С++, или я просто не полагаюсь на шаблоны?

4b9b3361

Ответ 1

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

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

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

Также обратите внимание, что С++ 0x вводит явные механизмы для лучшего управления созданием шаблона. Вы сможете явно создавать шаблоны и, самое главное, предотвращать инстанцирование в некоторых единицах компиляции. Однако большая часть этой работы уже выполняется большинством компиляторов без нашего ведома.

Итак, эмпирическое правило, поместите как можно больше кода (и включите директивы) в ваш .cpp. Если вы не можете, ну, вы не можете.

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

Ответ 2

Моим личным фаворитом является эта структура:

Заголовочный файл:

#ifndef HEADER_FILE
#define HEADER_FILE

template < typename T > 
class X { void method(); };

#include "header_impl.h"

#endif

Файл реализации:

#ifndef HEADER_IMPL_FILE
#define HEADER_IMPL_FILE

#include "header.h"

template < typename T > 
void X<T>::method() { }

#endif

Ответ 3

Я думаю, что действительно важно понять, что касается шаблонов, так это то, что перефразируя Bjarne Stroustrop, С++ действительно похож на несколько языков, включенных в один. Соглашения и идиомы шаблонов отличаются от условных обозначений "обычного" С++, почти как другой язык.

Совершенно неплохо отделить файлы заголовка и реализации в "регулярном" С++, потому что файлы заголовков сообщают компилятору, что вы будете поставлять в качестве реализации через некоторое время (во время соединения). Это важно, потому что это разделение является очень реальной вещью в большинстве распространенных операционных систем: время соединения может случиться, когда пользователь запускает программу. Вы можете скомпилировать реализацию в двоичные файлы (так, dll) и отправить неизменные заголовки для разработчиков, чтобы знать, как использовать вашу непрозрачную реализацию.

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

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

Ответ 4

Вот несколько методов, которые я использую при написании шаблонного кода, который перемещает реализации в файлы cpp:

  • Переместить части методов, которые не зависят от параметров шаблона, в отдельные, не-шаблонные вспомогательные функции или базовые классы. Затем тела могут перейти в свои собственные файлы cpp.

  • Напишите декларации об общих специализациях. Определения могут жить в своих собственных файлах cpp.

Ответ 5

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