У меня есть код шаблона, который я бы предпочел сохранить в файле CPP вместо встроенного в заголовок. Я знаю, что это можно сделать, если вы знаете, какие типы шаблонов будут использоваться. Например:
.h файл
class foo
{
public:
template <typename T>
void do(const T& t);
};
.cpp файл
template <typename T>
void foo::do(const T& t)
{
// Do something with t
}
template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);
Обратите внимание на последние две строки - функция шаблона foo:: do используется только с ints и std:: strings, поэтому эти определения означают, что приложение будет ссылаться.
Мой вопрос - это неприятный взлом или это будет работать с другими компиляторами/линкерами? Я использую этот код только с VS2008 в настоящий момент, но мне нужно будет переносить в другие среды.
Проблема, которую вы описываете, может быть решена путем определения шаблона в заголовке или с помощью описанного выше подхода.
Я рекомендую прочитать следующие пункты из С++ FAQ Lite:
Они подробно описывают эти (и другие) проблемы с шаблонами.
Для других пользователей на этой странице, интересующихся правилом синтаксиса (как и я) для явной специализации шаблона (или, по крайней мере, в VS2008), его следующее...
В вашем .h файле...
template<typename T>
class foo
{
public:
void bar(const T &t);
};
И в вашем .cpp файле
template <class T>
void foo<T>::bar(const T &t)
{ }
// Explicit template instantiation
template class foo<int>;
Этот код хорошо сформирован. Вам нужно только обратить внимание на то, что определение шаблона видимо в момент создания экземпляра. Чтобы процитировать стандарт, § 14.7.2.4:
Определение шаблона неэкспортируемой функции, неэкспортируемого шаблона функции участника или неэкспортируемой функции-члена или статического элемента данных шаблона класса должно присутствовать в каждой единицы перевода, в которой она явно создается.
Это должно работать нормально везде, где поддерживаются шаблоны. Явное создание экземпляра шаблона является частью стандарта С++.
Ваш пример верный, но не очень переносимый. Существует также несколько более чистый синтаксис, который можно использовать (как указано в @namespace-sid).
Предположим, что шаблонный класс является частью некоторой библиотеки, которая должна использоваться совместно. Должны ли компилироваться другие версии шаблонного класса? Предполагается ли, что администратор библиотеки предвидит все возможные шаблонные применения класса?
Альтернативный подход - небольшое изменение в том, что у вас есть: добавьте третий файл, который является файлом реализации/создания шаблона.
Файл foo.h
// Standard header file guards omitted
template <typename T>
class foo
{
public:
void bar(const T& t);
};
Файл foo.cpp
// Always include your headers
#include "foo.h"
template <typename T>
void foo::bar(const T& t)
{
// Do something with t
}
файл foo-impl.cpp
// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;
Одно из предостережений заключается в том, что вам нужно сообщить компилятору компилировать foo-impl.cpp
вместо foo.cpp
, поскольку компиляция последнего ничего не делает.
Конечно, вы можете иметь несколько реализаций в третьем файле или иметь несколько файлов реализации для каждого типа, который вы хотели бы использовать.
Это позволяет значительно повысить гибкость при совместном использовании шаблона для других целей.
Эта настройка также сокращает время компиляции для повторно используемых классов, потому что вы не перекомпилируете один и тот же заголовочный файл в каждой единицы перевода.
foo.cpp
), из которых на самом деле компилируются версии (в foo-impl.cpp
), и объявлений (в foo.h
). Мне не нравится, что большинство шаблонов C ++ полностью определены в заголовочных файлах. Это противоречит стандарту C / C ++ пар c[pp]/h
для каждого класса / пространства имен / независимо от того, какую группу вы используете. Люди, похоже, все еще используют монолитные заголовочные файлы просто потому, что эта альтернатива не используется широко или не известна.
Это определенно не неприятный взлом, но имейте в виду, что вам нужно будет сделать это (явная спецификация шаблона) для каждого класса/типа, который вы хотите использовать с данным шаблоном. В случае МНОГИХ типов, запрашивающих создание шаблона, может быть много строк в вашем .cpp файле. Чтобы исправить эту проблему, вы можете иметь TemplateClassInst.cpp в каждом проекте, который вы используете, чтобы у вас было больше контроля над тем, какие типы будут созданы. Очевидно, что это решение не будет идеальным (ака серебряная пуля), так как вы можете сломать ODR:).
В последнем стандарте есть ключевое слово (export
), которое поможет устранить эту проблему, но оно не реализовано ни в компилере, о котором я знаю, кроме Comeau.
Смотрите FAQ-lite об этом.
Да, это стандартный способ выполнения specializiation явного экземпляра. Как вы заявили, вы не можете создать этот шаблон с другими типами.
Изменить: исправлено на основе комментария.
Нет ничего плохого в примере, который вы указали. Но я должен сказать, что считаю, что неэффективно хранить определения функций в файле cpp. Я понимаю только необходимость отделить объявление и определение функции.
При использовании вместе с явным созданием класса, библиотека проверки возможностей Boost (BCCL) может помочь вам сгенерировать код функции шаблона в файлах cpp.
Время обновления! Создайте встроенный (.inl или, возможно, любой другой) файл и просто скопируйте все свои определения в нем. Не забудьте добавить шаблон над каждой функцией (template <typename T, ...>
). Теперь вместо включения файла заголовка в встроенный файл вы делаете обратное. Включите встроенный файл после объявления вашего класса (#include "file.inl"
).
Я действительно не знаю, почему никто не упомянул об этом. Я не вижу немедленных недостатков.
#include "file.inl"
, препроцессор собирается вставить содержимое file.inl
непосредственно в заголовок. Какой бы ни была причина, по которой вы хотели избежать реализации в заголовке, это решение не решает эту проблему.
template
вне линии. Я понимаю, почему люди хотят это сделать - добиться наибольшего паритета с объявлениями / определениями, не относящимися к шаблонам, чтобы интерфейсные объявления выглядели аккуратно и т. Д., - но это не всегда стоит хлопот. Это случай оценки компромиссов с обеих сторон и выбора наименее плохих . ... пока namespace class
станет вещью: O [ пожалуйста, будь вещью ]
do
в качестве идентификатора: p