Проблема шаблона вызывает ошибку компоновщика (C ++) - PullRequest
42 голосов
/ 28 октября 2009

Я очень мало представляю, что происходит с шаблонами C ++, но я пытаюсь реализовать функцию, которая ищет вектор для элемента, удовлетворяющего заданному свойству (в данном случае, поиск элемента с заданным именем). Моя декларация в моем .h файле выглядит следующим образом:

template <typename T>
T* find_name(std::vector<T*> v, std::string name);

Когда я компилирую, я получаю эту ошибку компоновщика при вызове функции:

Error   1   error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$find_name@VItem@@@@YAPAVItem@@V?$vector@PAVItem@@V?$allocator@PAVItem@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?get_item@Place@@QAEPAVItem@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) place.obj   Program2

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

Кроме того, связанный вопрос: есть ли способ сделать параметр шаблона таким, чтобы он был подклассом определенного класса, то есть шаблона?

Ответы [ 6 ]

73 голосов
/ 28 октября 2009

Вы должны иметь свои определения шаблонов, доступные на вызывающем сайте. Это означает, что нет .cpp файлов.

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

Шаблоны - это всего лишь резак печенья, потому что они не знают, что это за тип печенья. Он только говорит компилятору, как создать функцию, когда ему присваивается тип, но сам по себе он не может быть использован, потому что нет конкретного типа, с которым оперируют. Вы не можете приготовить формочку для печенья. Только когда у вас готово вкусное тесто для печенья (т. Е. С учетом теста [типа]), составленного из компилятора, вы можете разрезать печенье и приготовить его.

Аналогично, только когда вы на самом деле используете шаблон определенного типа, компилятор может сгенерировать реальную функцию и скомпилировать ее. Это не может сделать это, однако, если определение шаблона отсутствует. Вы должны переместить его в файл заголовка, чтобы вызывающая функция могла создать файл cookie.

47 голосов
/ 28 октября 2009

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

У вас есть два варианта. Поместите тело функции для шаблона функции в файл заголовка.

например. в заголовочном файле:

template <typename T>
inline T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

или явно создайте экземпляр шаблона в .cpp, где вы определили шаблон.

например. в исходном файле (вероятно, потребуется #include в файле, который определяет Item):

template <typename T>
T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

template Item* find_name<Item>(std::vector<Item*> v, std::string name);
11 голосов
/ 29 октября 2009

Ответы здесь великолепны.

Я просто добавлю, что часто в дополнение к .h и .cpp файлам в проекте. Вы часто найдете .inl файлов. Определения шаблонов будут помещены в файл .inl.

Эти .inl файлы означают inline и обычно включаются в файл .h с таким же префиксом имени внизу файла после всех объявлений заголовка. Это фактически делает их частью заголовочного файла, но отделяет объявления от любых определений.

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

5 голосов
/ 31 декабря 2012

Наткнулся на ту же проблему и нашел это, в котором говорится 3 обходных пути: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp

Среди них есть простой способ, где вы создаете в файле .cpp метод-пустышку, который вызывает функцию шаблона / класса с различными типами. Вставлено по ссылке:

// No need to call this TemporaryFunction() function, it's just to avoid link error.
void TemporaryFunction ()
{
    TestTemp<int> TempObj;
    TestTemp<float> TempObj2;
}
1 голос
/ 30 октября 2009

Я только что заметил, что у вас есть второй вопрос, который кажется без ответа:

Есть ли способ сделать параметр шаблона таким, чтобы он был подклассом определенного класса, т.е. шаблона?

Это возможно. Например, см. is_base_of в Boost.TypeTraits .

Однако мне любопытно: зачем вам это? Обычно требования шаблона к его параметрам относятся не к самому типу параметра, а к тому, какие выражения, включающие этот тип, являются допустимыми. Например, представьте, что у вас есть:

template<class T>
void foo(const T& t)
{
    if (t.foo()){
       t.bar("blah");
    }
}

Сказать, что T должен наследовать от чего-то вроде:

class HasFooAndBar
{
public:
  void foo()const;
  void bar(const char*)const;
};

ничего не приносит, потому что создание экземпляра функции все равно не удастся, если тип не поддерживает операции. Более того, это излишне ограничивает применимость foo(). Фактически, любые требования foo заключаются в том, что t.foo() and t.bar(const char*) являются допустимыми выражениями для const T. Например, этот тип не наследуется от HasFooAndBar и по-прежнему является допустимым параметром foo ():

struct DifferentFromHasFooAndBar
{
  bool foo()const;
  std::string bar(const std::string&)const;
};
0 голосов
/ 28 октября 2009

Вы поместили определение функции шаблона в файл cpp? Затем переместите его в заголовок и вставьте.

...