У меня есть некоторый шаблон кода, который я бы предпочел сохранить в файле 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, но хочу портировать на другие среды.
do
идентификатора: ptemplate class foo<int>;template class foo<std::string>;
в конце файла .cpp?Ответы:
Проблема, которую вы описываете, может быть решена путем определения шаблона в заголовке или с помощью подхода, который вы описали выше.
Я рекомендую прочитать следующие пункты из C ++ FAQ Lite :
Они подробно рассказывают об этих (и других) проблемах с шаблонами.
источник
Для других на этой странице, задающихся вопросом, каков правильный синтаксис (как я сделал) для явной специализации шаблона (или по крайней мере в VS2008), его следующее ...
В вашем .h файле ...
И в вашем .cpp файле
источник
Этот код правильно сформирован. Нужно только обратить внимание, что определение шаблона видно в момент его создания. Чтобы процитировать стандарт, § 14.7.2.4:
источник
a.cpp
(определяющие функциюa() {}
) иb.cpp
(определяющие функциюb() { a() }
), то это будет успешно соединено. Если я прав, то приведенная выше цитата может не подходить для типичного случая ... я что-то не так делаю?inline
функцииinline
. Причина в том, что без стандартизированного C ++ ABI трудно / невозможно определить, какой эффект это могло бы оказать в противном случае.Это должно работать нормально везде, где поддерживаются шаблоны. Явная реализация шаблона является частью стандарта C ++.
источник
Ваш пример правильный, но не очень переносимый. Существует также немного более чистый синтаксис, который можно использовать (как указано @ namespace-sid).
Предположим, что шаблонный класс является частью некоторой библиотеки, которая должна использоваться совместно. Должны ли компилироваться другие версии шаблонного класса? Должен ли сопровождающий библиотеки предвидеть все возможные шаблонные применения класса?
Альтернативный подход - это небольшая вариация того, что у вас есть: добавьте третий файл, который является файлом реализации / создания шаблона.
файл foo.h
файл foo.cpp
файл foo-impl.cpp
Один нюанс в том , что вы должны сообщить компилятору для компиляции
foo-impl.cpp
вместо того ,foo.cpp
как составление последнего ничего не делает.Конечно, вы можете иметь несколько реализаций в третьем файле или иметь несколько файлов реализации для каждого типа, который вы хотите использовать.
Это обеспечивает большую гибкость при совместном использовании шаблонного класса для других целей.
Эта настройка также уменьшает время компиляции для повторно используемых классов, потому что вы не перекомпилируете один и тот же заголовочный файл в каждом модуле перевода.
источник
foo.cpp
), из которых версии фактически скомпилированы (вfoo-impl.cpp
) и объявлений (вfoo.h
). Мне не нравится, что большинство шаблонов C ++ полностью определены в заголовочных файлах. Это противоречит стандарту пар C / C ++c[pp]/h
для каждого класса / пространства имен / независимо от того, какую группу вы используете. Люди, похоже, все еще используют монолитные заголовочные файлы просто потому, что эта альтернатива широко не используется и не известна.h/cpp
паре, хотя мне нужно было окружить исходный список экземпляров включенной защитой, но я все еще мог скомпилироватьfoo.cpp
как обычно. Я все еще довольно новичок в C ++, и мне было бы интересно узнать, есть ли у этого смешанного использования какие-либо дополнительные предостережения.foo.cpp
иfoo-impl.cpp
. Нет#include "foo.cpp"
вfoo-impl.cpp
файле; вместо этого добавьте объявление,extern template class foo<int>;
чтобыfoo.cpp
предотвратить создание экземпляра шаблона компилятором при компиляцииfoo.cpp
. Убедитесь, что система сборки создает оба.cpp
файла и передает оба объектных файла компоновщику. Это имеет несколько преимуществ: а) ясно,foo.cpp
что нет инстанцирования; б) изменения в foo.cpp не требуют перекомпиляции foo-impl.cpp.foo.cpp
вfoo_impl.h
иfoo-impl.cpp
толькоfoo.cpp
. Я также добавил бы typedefs для экземпляров изfoo.cpp
tofoo.h
, аналогичноusing foo_int = foo<int>;
. Хитрость заключается в том, чтобы предоставить пользователям два интерфейса заголовков на выбор. Когда пользователю нужно предварительно определенное создание, он включаетfoo.h
, когда пользователю нужно что-то не в порядке, он включаетfoo_impl.h
.Это определенно не противный хак, но имейте в виду, что вам придется делать это (явная специализация шаблона) для каждого класса / типа, который вы хотите использовать с данным шаблоном. В случае МНОГИХ типов, запрашивающих создание экземпляра шаблона, в вашем файле .cpp может быть МНОГО строк. Чтобы устранить эту проблему, вы можете иметь TemplateClassInst.cpp в каждом используемом вами проекте, чтобы вы могли лучше контролировать, какие типы будут создаваться. Очевидно, что это решение не будет идеальным (так называемая серебряная пуля), так как в итоге вы можете сломать ODR :).
источник
В последнем стандарте есть ключевое слово (
export
), которое поможет решить эту проблему, но оно не реализовано ни в одном из известных мне компиляторов, кроме Comeau.Смотрите FAQ-Lite об этом.
источник
Это стандартный способ определения функций шаблона. Я думаю, что есть три метода для определения шаблонов. Или, вероятно, 4. Каждый со своими плюсами и минусами.
Определите в определении класса. Мне это совсем не нравится, потому что я думаю, что определения классов предназначены только для справки и должны быть легко читаемыми. Однако определить шаблоны в классе гораздо сложнее, чем снаружи. И не все объявления шаблонов находятся на одном уровне сложности. Этот метод также делает шаблон настоящим шаблоном.
Определите шаблон в том же заголовке, но за пределами класса. Это мой любимый способ в большинстве случаев. Он сохраняет ваше определение класса в чистоте, шаблон остается истинным шаблоном. Однако это требует полного именования шаблонов, что может быть сложно. Также ваш код доступен всем. Но если вам нужен встроенный код, это единственный способ. Вы также можете сделать это, создав файл .INL в конце определения вашего класса.
Включите header.h и creation.CPP в ваш main.CPP. Я думаю, вот как это сделано. Вам не придется готовить какие-либо предварительные экземпляры, это будет вести себя как настоящий шаблон. Проблема с этим в том, что это не естественно. Обычно мы не включаем и не ожидаем включения исходных файлов. Я предполагаю, что поскольку вы включили исходный файл, функции шаблона могут быть встроены.
Этот последний метод, который был опубликован, определяет шаблоны в исходном файле, как и номер 3; но вместо того, чтобы включать исходный файл, мы предварительно создаем шаблоны для тех, которые нам понадобятся. У меня нет проблем с этим методом, и иногда он бывает полезен. У нас есть один большой код, он не может извлечь выгоду из встроенного кода, поэтому просто поместите его в файл CPP. И если мы знаем общие экземпляры и можем их предопределить. Это спасает нас от написания в основном одной и той же вещи 5, 10 раз. Этот метод имеет преимущество сохранения нашего кода в проприетарном виде. Но я не рекомендую помещать крошечные, регулярно используемые функции в файлы CPP. Как это снизит производительность вашей библиотеки.
Обратите внимание, я не знаю о последствиях раздутого файла obj.
источник
Да, это стандартный способ явной реализации
специализации. Как вы заявили, вы не можете создать экземпляр этого шаблона с другими типами.Изменить: исправлено на основе комментария.
источник
Давайте рассмотрим один пример, скажем, по какой-то причине вы хотите иметь шаблонный класс:
Если вы компилируете этот код с помощью Visual Studio - он работает «из коробки». gcc выдаст ошибку компоновщика (если один и тот же заголовочный файл используется из нескольких файлов .cpp):
Можно перенести реализацию в файл .cpp, но тогда вам нужно объявить класс следующим образом:
И тогда .cpp будет выглядеть так:
Без двух последних строк в заголовочном файле - gcc будет работать нормально, но Visual Studio выдаст ошибку:
Синтаксис класса шаблона является необязательным в случае, если вы хотите предоставить функцию через экспорт .dll, но это применимо только для платформы Windows - поэтому test_template.h может выглядеть так:
с .cpp файлом из предыдущего примера.
Это, однако, создает большую головную боль для компоновщика, поэтому рекомендуется использовать предыдущий пример, если вы не экспортируете функцию .dll.
источник
Время для обновления! Создайте встроенный (.inl или, возможно, любой другой) файл и просто скопируйте в него все свои определения. Обязательно добавьте шаблон над каждой функцией (
template <typename T, ...>
). Теперь вместо включения заголовочного файла во встроенный файл вы делаете обратное. Включите встроенный файл после объявления вашего класса (#include "file.inl"
).Я действительно не знаю, почему никто не упомянул это. Я не вижу непосредственных недостатков.
источник
#include "file.inl"
, препроцессор собирается вставить содержимоеfile.inl
непосредственно в заголовок. Какой бы ни была причина, по которой вы хотели избежать реализации в заголовке, это решение не решает эту проблему.template
определений. Я понимаю, почему люди хотят это сделать - добиться наибольшего паритета с объявлениями / определениями, не относящимися к шаблонам, чтобы интерфейсные объявления выглядели аккуратно и т. Д., - но это не всегда стоит хлопот. Это случай оценки компромиссов с обеих сторон и выбора наименее плохих . ... пока неnamespace class
станет вещью: O [ пожалуйста, будь вещью ]Нет ничего плохого в приведенном вами примере. Но я должен сказать, что считаю, что неэффективно хранить определения функций в файле cpp. Я понимаю только необходимость разделения объявления и определения функции.
При использовании вместе с явной реализацией классов, библиотека Boost Concept Check Library (BCCL) может помочь вам сгенерировать код функции шаблона в файлах cpp.
источник
Ничто из вышеперечисленного не сработало для меня, так что вот как у вас это решено, у моего класса только один шаблонный метод ..
.час
.cpp
это позволяет избежать ошибок компоновщика и вообще не вызывать TemporaryFunction
источник