2008-09-22 1 views
343

У меня есть код шаблона, который я бы предпочел сохранить в файле CPP вместо встроенного в заголовок. Я знаю, что это можно сделать, если вы знаете, какие типы шаблонов будут использоваться. Например:Сохранение определений функций шаблонов C++ в файле .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 :: сделать шаблон функции используется только в Интс и std :: strings, поэтому эти определения означают, что приложение будет связано.

Мой вопрос - это неприятный взлом или это будет работать с другими компиляторами/линкерами? Я использую этот код только с VS2008 в настоящий момент, но мне нужно будет подключиться к другим средам.

+15

Я понятия не имел, что это возможно - интересный трюк! Это помогло бы некоторым недавним задачам узнать об этом - ура! – xan 2008-09-22 16:00:23

+34

Вещь, которая топает меня, - это использование `do` в качестве идентификатора: p – Quentin 2015-01-14 10:00:46

+0

Я сделал somerhing, подобный gcc, но все еще исследую – Nick 2015-09-25 16:01:03

ответ

147

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

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

Они идут в много деталей об этих (и других) проблем шаблона ,

+26

Для того чтобы дополнить ответ, ссылка ссылается на вопрос положительно, то есть можно сделать то, что предложил Роб, и иметь код для переносимости. – ivotron 2011-05-01 21:46:35

+79

Можете ли вы просто опубликовать соответствующие части в самом ответе? Почему такая ссылка даже разрешена на SO. Я не знаю, что искать в этой ссылке, поскольку с тех пор она сильно изменилась. – Ident 2015-08-16 21:57:09

3

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

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

+0

Будучи разборчивым по терминологии, это «явная инстанция». – 2008-09-22 16:19:13

10

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

4

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

Об этом FAQ-lite.

+2

AFAIK, экспорт мертв, потому что они сталкиваются с новыми и новыми проблемами, каждый раз, когда они разрешают последнее, что делает общее решение более сложным. И ключевое слово «экспорт» не позволит вам «экспортировать» с CPP в любом случае (по-прежнему от Х. Саттера). Поэтому я говорю: не задерживайте дыхание ... – paercebal 2008-09-22 16:25:13

+2

Для реализации экспорта компилятор по-прежнему требует полного определения шаблона. Все, что вы получаете, это иметь его в виде скомпилированной формы. Но на самом деле нет никакого смысла. – 2012-03-12 16:56:27

+2

... и он * вышел * из стандарта, из-за чрезмерного усложнения для минимального усиления. – DevSolar 2015-08-26 13:04:41

12

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

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

0

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

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

5

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

81

Для других на этой странице, интересно, что правильный синтаксис (как это сделал я) для явного специализации шаблона (или, по крайней мере, в 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>; 
-2

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

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

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, поскольку компиляция последнего ничего не делает.

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

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

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

Смежные вопросы