模板问题导致链接器错误(C++)

mar*_*olk 44 c++ templates compiler-errors

我几乎不知道C++模板会发生什么,但我正在尝试实现一个函数,它在向量中搜索满足给定属性的元素(在这种情况下,搜索名称给定的元素).我在.h文件中的声明如下:

template <typename T>
T* find_name(std::vector<T*> v, std::string name);
Run Code Online (Sandbox Code Playgroud)

当我编译时,我在调用函数时遇到此链接器错误:

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
Run Code Online (Sandbox Code Playgroud)

同样,我是模板的新手,所以我不知道会发生什么.我通过谷歌找到的LNK2019的所有实例都没有使用正确的库,但由于这是我自己的功能,我不明白为什么会发生这种情况.

另外,一个相关的问题:是否有办法制作模板参数,以便它必须是某个类的子类,即模板?

GMa*_*ckG 74

您必须在调用站点上提供模板定义.这意味着没有.cpp文件.

原因是模板无法编译.将功能视为cookie,编译器是烤箱.

模板只是一个千篇一律,因为他们不知道它们是什么类型的cookie.它只告诉编译器在给定类型时如何创建函数,但是它本身不能使用它,因为没有操作的具体类型.你不能煮饼干.只有当您准备好美味的饼干面团时(即,给予编辑器面团[类型]),您可以切割饼干并烹饪它.

同样,只有当您实际使用具有特定类型的模板时,编译器才能生成实际函数并进行编译.但是,如果缺少模板定义,则无法执行此操作.您必须将其移动到头文件中,因此函数的调用者可以创建cookie.

  • 说不能将模板定义放入.cpp文件中是不正确的.有关问题的更完整解释,请参阅下面的Charles Bailey的答案. (4认同)
  • 函数模板“不是”函数。函数模板是*模板*。他们遵守自己的规则。 (3认同)

CB *_*ley 51

您可能正在缺少有效的实例化.如果将模板定义放在单独的.cpp文件中,则当编译器编译该文件时,它可能不知道您需要哪个实例.相反,在将实例化模板函数的正确版本的调用站点,如果函数体的定义不可用,则编译器将不具有实例化所需特化的信息.

你有两个选择.将函数模板的函数体放在头文件中.

例如在头文件中:

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

或者在您定义模板的.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);
Run Code Online (Sandbox Code Playgroud)

  • 我觉得这比接受的答案更有用,因为有关强制在C++文件中实例化的重要提示. (4认同)

cho*_*ida 11

这里的答案很棒.

我只想补充一点,这通常是除了项目中的文件.h.cpp文件之外的原因.你经常会找到.inl文件.模板定义将进入.inl文件.

这些.inl文件意味着内联,并且通常.h在所有标头声明之后由文件底部的相同名称前缀的文件包含.这有效地使它们成为头文件的一部分,但将声明与任何定义分开.

由于它们是美化的头文件,您应该采取与常规头文件相同的预防措施,即包括警卫等.

  • 这些`.inl`文件是否受C++标准支持,或者它们是否依赖于编译器? (2认同)

KBo*_*Bog 5

偶然发现相同的问题,发现它指出了3种解决方法:http : //www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-ah-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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我真的很喜欢这种方法,但是有没有一种方法可以确保编译器不会将其优化呢?我曾经尝试过-O3优化,然后是“未定义符号” (4认同)