Всегда учитывая, что следующий заголовок, содержащий мой шаблонный класс, включен как минимум в два .CPP
файла, этот код компилируется правильно:
template <class T>
class TClass
{
public:
void doSomething(std::vector<T> * v);
};
template <class T>
void TClass<T>::doSomething(std::vector<T> * v) {
// Do something with a vector of a generic T
}
template <>
inline void TClass<int>::doSomething(std::vector<int> * v) {
// Do something with a vector of int's
}
Но обратите внимание на inline в методе специализации. Это необходимо, чтобы избежать ошибки компоновщика (в VS2008 это LNK2005) из-за того, что метод определен более одного раза. Я понимаю это, потому что AFAIK полная специализация шаблона совпадает с простым определением метода.
Итак, как мне это удалить inline
? Код не должен дублироваться при каждом его использовании. Я искал Google, читал некоторые вопросы здесь, в SO, и пробовал многие из предложенных решений, но ни одно из них не было успешно построено (по крайней мере, не в VS 2008).
Благодарность!
Ответы:
Как и в случае с простыми функциями, вы можете использовать объявление и реализацию. Поместите в объявление заголовка:
template <> void TClass<int>::doSomething(std::vector<int> * v);
и поместите реализацию в один из ваших cpp-файлов:
template <> void TClass<int>::doSomething(std::vector<int> * v) { // Do somtehing with a vector of int's }
Не забудьте удалить inline (забыл и подумал, что это решение не сработает :)). Проверено на VC ++ 2005
источник
inline
время копирования / вставки. Вот так это сработало!Вам необходимо переместить определение специализации в файл CPP. Специализация функции-члена класса шаблона разрешена, даже если функция не объявлена как шаблон.
источник
Нет причин удалять ключевое слово inline.
Это никак не меняет смысла кода.
источник
inline
ключевое слово приводит к тому, что функция фактически встроена (в стандарте сказано, что компилятор должен воспринимать это как подсказку), то эти лишние копии не могут быть удалены. Однако это всего лишь намек на встраивание (его основной эффект - сказать «не генерировать ошибки при конфликтах ссылок определенным образом»)Если вы хотите удалить встроенный по какой-либо причине, решение maxim1000 вполне подходит.
Однако в вашем комментарии, похоже, вы считаете, что ключевое слово inline означает, что функция со всем его содержимым всегда встроена, но AFAIK, который на самом деле очень сильно зависит от оптимизации вашего компилятора.
Цитата из C ++ FAQ
Итак, если вы не знаете, что эта функция на самом деле раздувает ваш исполняемый файл или если вы не хотите удалить ее из заголовка определения шаблона по другим причинам, вы можете оставить его там, где он есть, без какого-либо вреда.
источник
Я хотел бы добавить, что все еще есть веская причина оставить
inline
ключевое слово там, если вы собираетесь оставить также специализацию в файле заголовка.Ссылка: https://stackoverflow.com/a/4445772/1294184
источник
Это немного ОТ, но я подумал, что оставлю это здесь, на случай, если это поможет кому-то другому. Я искал в гугле специализацию шаблонов, которая привела меня сюда, и хотя ответ @ maxim1000 правильный и в конечном итоге помог мне разобраться в моих проблемах, я не думал, что это достаточно ясно.
Моя ситуация немного отличается (но достаточно похожа, чтобы оставить этот ответ, я думаю), чем у OP. По сути, я использую стороннюю библиотеку со всеми видами классов, которые определяют «типы статуса». Сердцем этих типов являются просто
enum
s, но все классы наследуются от общего (абстрактного) родителя и предоставляют различные служебные функции, такие как перегрузка оператора иstatic toString(enum type)
функция. Каждый статусenum
отличается друг от друга и не связан. Например, у одногоenum
есть поляNORMAL, DEGRADED, INOPERABLE
, у другого -AVAILBLE, PENDING, MISSING
и т. Д. Моя программа отвечает за управление разными типами статусов для разных компонентов. Оказалось, что я хотел использоватьtoString
функции для этихenum
классы, но поскольку они абстрактны, я не мог создать их экземпляры напрямую. Я мог бы расширить каждый класс, который хотел бы использовать, но в конечном итоге я решил создатьtemplate
класс, в которомtypename
будет любой конкретный статус, которыйenum
меня заботит. Вероятно, это решение можно обсудить, но я чувствовал, что это было намного меньше работы, чем расширение каждого абстрактногоenum
класса моим собственным и реализация абстрактных функций. И, конечно же, в моем коде я просто хотел иметь возможность вызывать.toString(enum type)
и печатать строковое представление этогоenum
. Поскольку все ониenum
были совершенно не связаны между собой, у каждого из них были своиtoString
функции, которые (после некоторых исследований, которые я узнал) должны были вызываться с использованием специализации шаблонов. Это привело меня сюда. Ниже приводится MCVE того, что мне пришлось сделать, чтобы это работало правильно. И на самом деле мое решение немного отличалось от @ maxim1000.Это (значительно упрощенный) заголовочный файл для
enum
s. На самом деле каждыйenum
класс был определен в собственном файле. Этот файл представляет файлы заголовков, которые предоставляются мне как часть библиотеки, которую я использую:// file enums.h #include <string> class Enum1 { public: enum EnumerationItem { BEARS1, BEARS2, BEARS3 }; static std::string toString(EnumerationItem e) { // code for converting e to its string representation, // omitted for brevity } }; class Enum2 { public: enum EnumerationItem { TIGERS1, TIGERS2, TIGERS3 }; static std::string toString(EnumerationItem e) { // code for converting e to its string representation, // omitted for brevity } };
добавив эту строку, чтобы разделить следующий файл на другой блок кода:
// file TemplateExample.h #include <string> template <typename T> class TemplateExample { public: TemplateExample(T t); virtual ~TemplateExample(); // this is the function I was most concerned about. Unlike @maxim1000's // answer where (s)he declared it outside the class with full template // parameters, I was able to keep mine declared in the class just like // this std::string toString(); private: T type_; }; template <typename T> TemplateExample<T>::TemplateExample(T t) : type_(t) { } template <typename T> TemplateExample<T>::~TemplateExample() { }
следующий файл
// file TemplateExample.cpp #include <string> #include "enums.h" #include "TemplateExample.h" // for each enum type, I specify a different toString method, and the // correct one gets called when I call it on that type. template <> std::string TemplateExample<Enum1::EnumerationItem>::toString() { return Enum1::toString(type_); } template <> std::string TemplateExample<Enum2::EnumerationItem>::toString() { return Enum2::toString(type_); }
следующий файл
// and finally, main.cpp #include <iostream> #include "TemplateExample.h" #include "enums.h" int main() { TemplateExample<Enum1::EnumerationItem> t1(Enum1::EnumerationItem::BEARS1); TemplateExample<Enum2::EnumerationItem> t2(Enum2::EnumerationItem::TIGERS3); std::cout << t1.toString() << std::endl; std::cout << t2.toString() << std::endl; return 0; }
и это выводит:
Понятия не имею, является ли это идеальным решением моей проблемы, но у меня оно сработало. Теперь, независимо от того, сколько типов перечисления я в конечном итоге использую, все, что мне нужно сделать, это добавить несколько строк для
toString
метода в файл .cpp, и я могу использовать метод, уже определенный в библиотеках,toString
не реализуя его самостоятельно и не расширяя каждыйenum
класс, который я хочу использовать.источник