2009-10-28 3 views
31

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

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> >)" ([email protected]@@@@[email protected]@[email protected]@@[email protected]@@@[email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@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> >)" ([email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z) place.obj Program2 

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

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

+0

какой компилятор вы используете? некоторые компиляторы не позволяют вам разделять объявление и определение на отдельные файлы для шаблонов. – Jordan

+0

Действительно ли вы написали реализацию для своей функции шаблона? – begray

+2

Вы также можете рассмотреть использование std :: find или std :: find_if –

ответ

59

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

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

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

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

+0

Спасибо. Теперь я понимаю это, но, боюсь, я не совсем понимаю, почему он может сделать это для регулярных функций, но не для функций шаблонов, и я полагаю, что могу не до конца понять C++. – marsolk

+0

Шаблоны являются шаблонами, несколько похожими на макросы, только лучше (и хуже;). До тех пор, пока вы не используете их, компилятор не обязан выполнять макроподобную замену с заданным типом (-ами) и создавать фактическую функцию. Теперь, когда компилятор попадает в этот экземпляр, он может или не может иметь шаблон в руке. Если это вы не попали в блокпост. – dirkgently

+1

Шаблоны функций - это * не * функции. Шаблоны функций - это * шаблоны *. Они подчиняются своим собственным правилам. – AnT

0

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

+0

Нет, это не требуется. См. Ответ Чарльза Бейли выше. –

29

Возможно, вы страдаете от отсутствия действительного экземпляра. Если вы поместите определение вашего шаблона в отдельный .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); 
+1

Я нахожу это более полезным, чем принятый ответ из-за этого замечательного совета о форсировании экземпляра в файле C++. – ancientHacker

9

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

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

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

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

+0

Являются ли эти файлы '.inl' поддерживаемыми стандартом C++ или они зависят от компилятора? – kim366

0

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

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

Это возможно. Например, см. 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*) являются корректными выражениями на константный Т. Например, этот тип не наследуется от HasFooAndBar и до сих пор действует Foo() параметр:

struct DifferentFromHasFooAndBar 
{ 
    bool foo()const; 
    std::string bar(const std::string&)const; 
}; 
2

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

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

// No need to call this TemporaryFunction() function, it's just to avoid link error. 
void TemporaryFunction() 
{ 
    TestTemp<int> TempObj; 
    TestTemp<float> TempObj2; 
} 
+2

Мне очень нравится этот метод, но есть ли способ убедиться, что компилятор не будет оптимизировать эту вещь? Я однажды попробовал -O3 оптимизации, а затем 'undefined symbol' – darwinsenior

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