【问题标题】:Template issue causes linker error (C++) [duplicate]模板问题导致链接器错误(C++)[重复]
【发布时间】:2010-12-11 00:29:33
【问题描述】:

我对 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 实例都是关于没有使用正确的库,但由于这是我自己的函数,我不明白为什么会发生这种情况。

还有一个相关的问题:有没有办法让模板参数成为某个类的子类,即模板?

【问题讨论】:

  • 你用的是什么编译器?一些编译器会阻止您将声明和定义分离到单独的模板文件中。
  • 你真的为你的模板函数写了一个实现吗?
  • 你也可以考虑使用 std::find 或 std::find_if
  • 按值传递 v 和 name 的成本很高。改为通过 const 引用传递它们。
  • 你说得对,埃里克,我还不太会使用 C++。至于find,看起来我应该更努力地寻找,但是如果它没有找到它为什么会最后返回呢?不存在的元素不应该有更好的返回值吗?

标签: c++ templates compiler-errors


【解决方案1】:

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

原因是模板无法编译。把函数想象成 cookie,编译器就是一个烤箱。

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

同样,只有当你实际使用具有某种类型的模板时,编译器才能生成实际的函数,并编译它。但是,如果缺少模板定义,它就不能这样做。你必须把它移到头文件中,这样函数的调用者才能制作cookie。

【讨论】:

  • 谢谢。我现在明白了,但恐怕我不太明白为什么它可以对常规函数执行此操作,但不能对模板函数执行此操作,我想这可以归结为对 C++ 的不完全理解。
  • 模板是样板,有点像宏,只是更好(和更糟;)。除非您使用它们,否则编译器不需要使用给定类型进行类似宏的替换并创建实际函数。现在,当编译器遇到这个实例化时,它可能有也可能没有样板。如果没有,你就会遇到障碍。
  • 函数模板是不是函数。函数模板是模板。他们遵守自己的规则。
  • 说不能将模板定义放入 .cpp 文件是不正确的。有关该问题的更完整解释,请参阅下面 Charles Bailey 的回答。
  • 我最初将定义放在 cpp 上以打破循环引用,将两个类放在一个 .h 文件中。我最终在第二类之后复制了第一类的函数定义。
【解决方案2】:

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

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

例如在头文件中:

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

或在您定义模板的 .cpp 中显式实例化模板。

例如在源文件中(可能需要#includeing 定义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);

【讨论】:

  • 我发现这比接受的答案更有用,因为关于在 C++ 文件中强制实例化的重要提示。
  • 工人就像一个魅力!
【解决方案3】:

这里的答案很棒。

我只是补充一点,这通常是为什么在项目中除了.h.cpp 文件。你会经常找到.inl 文件。模板定义将进入.inl 文件。

这些.inl 文件表示内联,通常会包含在文件底部的同名前缀的.h 文件中,位于所有标头声明之后。这有效地使它们成为头文件的一部分,但将声明与任何定义分开。

由于它们是美化的头文件,因此您应该采取与常规头文件相同的预防措施,即包含保护等。

【讨论】:

  • 这些 .inl 文件是否受 C++ 标准支持,还是依赖于编译器?
【解决方案4】:

偶然发现了同样的问题,发现了 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;
}

【讨论】:

  • 我真的很喜欢这个方法,但是有没有办法确保编译器不会优化这个东西呢?我曾经试过-O3优化,然后undefined symbol
【解决方案5】:

我刚刚注意到您的第二个问题似乎没有得到解答:

有没有办法让模板参数成为某个类的子类,即模板?

这是可能的。例如,参见Boost.TypeTraits 中的is_base_of

但是,我很好奇:你为什么想要那个?通常,模板对其参数的要求不在于参数的类型本身,而在于涉及该类型的表达式是否合法。例如,假设您有:

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;
};

【讨论】:

    【解决方案6】:

    您是否将模板函数定义放在 cpp 文件中?然后将其移动到标题并内联它。

    【讨论】:

    • 不,这不是必需的。请参阅上面查尔斯·贝利的回答。
    猜你喜欢
    • 2010-11-24
    • 2015-01-13
    • 1970-01-01
    • 2013-07-29
    • 2014-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-16
    相关资源
    最近更新 更多