Прежде всего, для полноты, я рассмотрю простое решение: используйте шаблонный код только при необходимости и основывайте его на не шаблонном коде (с реализацией в своем собственном исходном файле).
Однако я подозреваю, что реальная проблема заключается в том, что вы используете универсальное программирование, как если бы вы использовали типичное ОО-программирование и получили раздутый класс.
Давайте рассмотрим пример:
// "bigArray/bigArray.hpp"
template <class T, class Allocator>
class BigArray
{
public:
size_t size() const;
T& operator[](size_t index);
T const& operator[](size_t index) const;
T& at(size_t index);
T const& at(size_t index);
private:
// impl
};
Это вас шокирует? Возможно нет. Это кажется довольно минималистским в конце концов. Дело в том, что это не так. Методы at
могут быть разложены без потери общности:
// "bigArray/at.hpp"
template <class Container>
typename Container::reference_type at(Container& container,
typename Container::size_type index)
{
if (index >= container.size()) throw std::out_of_range();
return container[index];
}
template <class Container>
typename Container::const_reference_type at(Container const& container,
typename Container::size_type index)
{
if (index >= container.size()) throw std::out_of_range();
return container[index];
}
Хорошо, это немного меняет вызов:
// From
myArray.at(i).method();
// To
at(myArray,i).method();
Однако благодаря поиску Кенига вы можете назвать их неквалифицированными, если поместите их в одно и то же пространство имен, так что это просто привычка.
Пример надуман, но общая точка зрения остаётся. Обратите внимание, что из-за его универсальности at.hpp
никогда не приходилось включать bigArray.hpp
и все равно будет генерировать такой же жесткий код, как если бы он был методом-членом, просто мы можем вызвать его в других контейнерах, если захотим.
И теперь пользователю BigArray
не нужно включать at.hpp
, если он его не использует ... таким образом, уменьшая свои зависимости и не оказывая влияния, если вы изменяете код в этом файле: например, alter std::out_of_range
вызов для указания имени файла и номера строки, адреса контейнера, его размера и индекса, к которому мы пытались получить доступ.
Другое (не столь очевидное) преимущество заключается в том, что если когда-либо будет нарушено ограничение целостности BigArray
, то at
явно не будет иметь смысла, поскольку не может связываться с внутренностями класса, уменьшая таким образом количество подозреваемые.
Это рекомендуется многими авторами, такими как Herb Sutters в C ++ Стандарты кодирования :
Пункт 44: Предпочитать писать функции, не являющиеся членами группы
и широко используется в Boost ... Но вы должны изменить свои привычки кодирования!
Тогда, конечно, вам нужно включить только то, от чего вы зависите, должны быть статические анализаторы кода C ++, которые сообщают о включенных, но неиспользуемые заголовочные файлы, которые могут помочь понять это.