Хранение определений функций шаблона C ++ в файле .CPP

348

У меня есть код шаблона, который я бы предпочел сохранить в файле 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 в настоящий момент, но мне нужно будет переносить в другие среды.

  • 18
    Я понятия не имел, что это возможно - интересный трюк! Это помогло бы некоторым недавним задачам, знающим об этом - ура!
  • 49
    Меня пугает использование do в качестве идентификатора: p
Показать ещё 3 комментария
Теги:
templates

10 ответов

140
Лучший ответ

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

Я рекомендую прочитать следующие пункты из С++ FAQ Lite:

Они подробно описывают эти (и другие) проблемы с шаблонами.

  • 29
    Просто чтобы дополнить ответ, ссылка, на которую есть ссылка, отвечает на вопрос положительно, то есть можно сделать то, что предложил Роб, и иметь код для переносимости.
  • 107
    Вы можете просто опубликовать соответствующие части в самом ответе? Почему такая ссылка даже разрешена на SO. Я понятия не имею, что искать в этой ссылке, так как она сильно изменилась с тех пор.
82

Для других пользователей на этой странице, интересующихся правилом синтаксиса (как и я) для явной специализации шаблона (или, по крайней мере, в 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>;
  • 9
    Вы имеете в виду "для явной специализации шаблона CLASS". В таком случае это будет охватывать каждую функцию, которая есть в шаблонном классе?
12

Этот код хорошо сформирован. Вам нужно только обратить внимание на то, что определение шаблона видимо в момент создания экземпляра. Чтобы процитировать стандарт, § 14.7.2.4:

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

  • 1
    Что означает не экспортируемый ?
  • 1
    @Dan Видимый только внутри модуля компиляции, а не снаружи. Если вы связываете несколько модулей компиляции вместе, экспортированные символы могут использоваться между ними (и должны иметь одно или, по крайней мере, в случае шаблонов, согласованные определения, в противном случае вы столкнетесь с UB).
Показать ещё 5 комментариев
10

Это должно работать нормально везде, где поддерживаются шаблоны. Явное создание экземпляра шаблона является частью стандарта С++.

5

Ваш пример верный, но не очень переносимый. Существует также несколько более чистый синтаксис, который можно использовать (как указано в @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, поскольку компиляция последнего ничего не делает.

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

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

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

  • 0
    что это тебе покупает? Вам все еще нужно отредактировать foo-impl.cpp, чтобы добавить новую специализацию.
  • 0
    Разделение деталей реализации (или определений в foo.cpp ), из которых на самом деле компилируются версии (в foo-impl.cpp ), и объявлений (в foo.h ). Мне не нравится, что большинство шаблонов C ++ полностью определены в заголовочных файлах. Это противоречит стандарту C / C ++ пар c[pp]/h для каждого класса / пространства имен / независимо от того, какую группу вы используете. Люди, похоже, все еще используют монолитные заголовочные файлы просто потому, что эта альтернатива не используется широко или не известна.
Показать ещё 1 комментарий
5

Это определенно не неприятный взлом, но имейте в виду, что вам нужно будет сделать это (явная спецификация шаблона) для каждого класса/типа, который вы хотите использовать с данным шаблоном. В случае МНОГИХ типов, запрашивающих создание шаблона, может быть много строк в вашем .cpp файле. Чтобы исправить эту проблему, вы можете иметь TemplateClassInst.cpp в каждом проекте, который вы используете, чтобы у вас было больше контроля над тем, какие типы будут созданы. Очевидно, что это решение не будет идеальным (ака серебряная пуля), так как вы можете сломать ODR:).

  • 0
    Вы уверены, что это сломает ODR? Если строки экземпляра в TemplateClassInst.cpp ссылаются на идентичный исходный файл (содержащий определения функции шаблона), разве это не гарантирует, что не нарушит ODR, поскольку все определения идентичны (даже если повторяются)?
  • 0
    Пожалуйста, что такое ODR?
4

В последнем стандарте есть ключевое слово (export), которое поможет устранить эту проблему, но оно не реализовано ни в компилере, о котором я знаю, кроме Comeau.

Смотрите FAQ-lite об этом.

  • 2
    AFAIK, экспорт мертв, потому что они сталкиваются с новыми и новыми проблемами, каждый раз, когда они решают последнюю, делая общее решение все более и более сложным. И ключевое слово «export» не позволит вам «экспортировать» из CPP в любом случае (все равно из H. Sutter's в любом случае). Поэтому я говорю: не задерживай дыхание ...
  • 2
    Для реализации экспорта компилятору по-прежнему требуется полное определение шаблона. Все, что вы получаете, это иметь своего рода скомпилированную форму. Но на самом деле нет никакого смысла в этом.
Показать ещё 1 комментарий
3

Да, это стандартный способ выполнения specializiation явного экземпляра. Как вы заявили, вы не можете создать этот шаблон с другими типами.

Изменить: исправлено на основе комментария.

  • 0
    Быть разборчивым в терминологии - это «явная реализация».
0

Нет ничего плохого в примере, который вы указали. Но я должен сказать, что считаю, что неэффективно хранить определения функций в файле cpp. Я понимаю только необходимость отделить объявление и определение функции.

При использовании вместе с явным созданием класса, библиотека проверки возможностей Boost (BCCL) может помочь вам сгенерировать код функции шаблона в файлах cpp.

  • 7
    Что в этом неэффективного?
-2

Время обновления! Создайте встроенный (.inl или, возможно, любой другой) файл и просто скопируйте все свои определения в нем. Не забудьте добавить шаблон над каждой функцией (template <typename T, ...>). Теперь вместо включения файла заголовка в встроенный файл вы делаете обратное. Включите встроенный файл после объявления вашего класса (#include "file.inl").

Я действительно не знаю, почему никто не упомянул об этом. Я не вижу немедленных недостатков.

  • 24
    Непосредственный недостаток заключается в том, что он в основном такой же, как просто определение функций шаблона непосредственно в заголовке. Как только вы #include "file.inl" , препроцессор собирается вставить содержимое file.inl непосредственно в заголовок. Какой бы ни была причина, по которой вы хотели избежать реализации в заголовке, это решение не решает эту проблему.
  • 5
    - и означает, что вы, технически неоправданно, обременяете себя задачей написания всего подробного, сногсшибательного шаблона, необходимого для определения template вне линии. Я понимаю, почему люди хотят это сделать - добиться наибольшего паритета с объявлениями / определениями, не относящимися к шаблонам, чтобы интерфейсные объявления выглядели аккуратно и т. Д., - но это не всегда стоит хлопот. Это случай оценки компромиссов с обеих сторон и выбора наименее плохих . ... пока namespace class станет вещью: O [ пожалуйста, будь вещью ]
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню