我很想不到有关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
同样,我是模板的新手,所以我不知道会发生什么。我通过Google找到LNK2019的所有实例都没有使用正确的库,但由于这是我自己的功能,我不明白为什么会发生这种情况。
另外,一个相关的问题:是否有办法制作模板参数,使其必须是某个类的子类,即模板?
答案 0 :(得分:73)
您必须在呼叫站点上提供模板定义。这意味着没有.cpp
个文件。
原因是模板无法编译。将功能视为cookie,编译器就是烤箱。
模板只是一个千篇一律,因为他们不知道它们是什么类型的cookie。它只告诉编译器在给定类型时如何创建函数,但是它本身不能使用它,因为没有操作的具体类型。你不能煮饼干。只有当您准备好美味的饼干面团时(即,给予编辑器面团[类型]),您才能切割饼干并烹饪。
同样,只有当您实际使用具有特定类型的模板时,编译器才能生成实际函数并进行编译。但是,如果缺少模板定义,则无法执行此操作。您必须将其移动到头文件中,因此函数的调用者可以创建cookie。
答案 1 :(得分:46)
您可能缺少有效的实例化。如果将模板定义放在单独的.cpp文件中,当编译器编译该文件时,它可能不知道您需要哪个实例。相反,在将实例化模板函数的正确版本的调用站点,如果函数体的定义不可用,则编译器将不具有实例化所需特化的信息。
您有两种选择。将函数模板的函数体放在头文件中。
e.g。在头文件中:
template <typename T>
inline T* find_name(std::vector<T*> v, std::string name)
{
// ...
}
或在您定义模板的.cpp中显式实例化模板。
e.g。在源文件中(可能需要#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);
答案 2 :(得分:11)
这里的答案很棒。
我只想补充一点,这通常是项目中.h
和.cpp
文件的原因。您经常会找到.inl
个文件。模板定义将进入.inl
文件。
这些.inl
文件意味着内联,并且通常在所有标头声明之后由文件底部的相同名称前缀的.h
文件包含。这有效地使它们成为头文件的一部分,但将声明与任何定义分开。
由于它们是美化的头文件,因此您应采取与常规头文件相同的预防措施,即包括警卫等。
答案 3 :(得分:4)
偶然发现了同样的问题并发现了3个解决方法: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp
其中很简单,你可以创建一个&#34;虚拟&#34; .cpp文件中的方法,它调用具有不同类型的模板/类函数。粘贴链接:
// No need to call this TemporaryFunction() function, it's just to avoid link error.
void TemporaryFunction ()
{
TestTemp<int> TempObj;
TestTemp<float> TempObj2;
}
答案 4 :(得分:1)
我刚注意到你有第二个问题似乎没有答案:
有没有办法制作模板参数,以便它必须是某个类的子类,即模板?
有可能。例如,请参阅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;
};
答案 5 :(得分:0)
您是否将模板函数定义放在cpp文件中?然后将其移动到标题并内联它。