Как согласовать идиому C ++ разделения заголовка / источника с шаблонами? - PullRequest
21 голосов
/ 09 февраля 2011

Я немного интересуюсь этим бизнесом шаблонов.

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

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

Ответы [ 5 ]

19 голосов
/ 09 февраля 2011

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

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

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

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

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

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

7 голосов
/ 09 февраля 2011

Мой личный фаворит - эта структура:

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

#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
2 голосов
/ 10 февраля 2011

Я думаю, что действительно важно понимать шаблоны в том, что, перефразируя Бьярна Страустропа, C ++ действительно похож на несколько языков, объединенных в один. Соглашения и идиомы шаблонов отличаются от соглашений по написанию «обычного» C ++, почти как другой язык.

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

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

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

1 голос
/ 10 февраля 2011

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

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

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

0 голосов
/ 10 февраля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...