【问题标题】:C++ (gcc): undefined reference to `Stack<int>::Stack(int)' [duplicate]C ++(gcc):对`Stack<int> :: Stack(int)'的未定义引用[重复]
【发布时间】:2011-10-11 18:55:16
【问题描述】:

可能重复:
Why can templates only be implemented in the header file?

我已经为此苦苦挣扎了一段时间,并且在这里查看了几个问题,但是作为 C++ 新手,我无法理解我错在哪里。

这是代码,我从this page 获取并尝试使其工作,但到目前为止我还没有幸运:

stack.h

#ifndef STACK_H
#define STACK_H
template <class T>
class Stack {
  public:
    Stack(int n);
    ~Stack() { delete[] s; };
  private:
    T* s;
    int _top;
    int _size;
};
#endif // STACK_H

stack.cpp

#include "stack.h"
template <class T>
Stack<T>::Stack(int n) {
  _size = n;
  _top = -1;
  s = new T[_size];
}

ma​​in.cpp

#include <iostream>
#include "stack.h"
using namespace std;
int main() {
  Stack<int> s(10); // undefined reference to `Stack<int>::Stack(int)'
  return 0;
}

当我编译 (gcc 4.5.2) 时出现一个错误:undefined reference to Stack&lt;int&gt;::Stack(int)。我已经尝试了几件事,但没有任何真正的知识来支持我的工作。如果有人能向我解释发生了什么,我将非常感激。

【问题讨论】:

  • 好吧,它不是重复的,虽然它确实解决了我的问题。我正在寻找错误(undefined reference),所以我之前没有找到那个问题。谢谢!
  • 好像有很多新人开始学习C++了……这是C++11的影响还是什么?
  • @Vlad:复制构造函数、移动构造函数、复制赋值运算符、移动赋值运算符、析构函数。 (4.5 使用复制和交换:复制构造函数、移动构造函数、赋值操作符、析构函数、交换。)
  • @GMan 我实际上是忍者在我的答案中编辑了五音符规则,弗拉德实际上指出我最初错过了它。
  • 您是否尝试过使用std::stack

标签: c++ gcc


【解决方案1】:

如果模板类定义是专门定义的,则只能在 cpp 文件中包含模板类定义 - 即您知道 T 是什么。

除此之外,您的案例属于这里,您的模板类的所有定义都必须放在头文件中。每次声明或定义新实例时,编译器都必须知道这些,因为类型和行为都会发生变化。 cpp 文件中的定义意味着不同的翻译单元,因此编译器不可能知道您尝试应用于模板的每个 T 的行为。

【讨论】:

  • 非常感谢。这很清楚,我现在明白了。
【解决方案2】:

“stack.cpp”中没有要编译的内容。模板仅在实例化时才被编译。因此链接器找不到这个从未编译过的函数。

您无法使用模板将头文件和源文件中的声明和实现真正分开。

您可以做的是将“stack.cpp”复制并粘贴到“stack.h”的末尾。或者在“stack.h”的末尾包含“stack.cpp”,而不是相反,达到相同的效果。在后一种情况下,更改“cpp”文件的扩展名可能是明智之举(请参阅Including .cpp at end of template header file

【讨论】:

    【解决方案3】:

    编译器在创建模板类时必须拥有所有相关信息,因此模板通常在其头文件中完全实现。

    有几种方法可以实现这一点,内联函数,在头文件中定义模板函数,包括在单独的文件中实现(在头文件的末尾,带有#include)等。

    这里是similar question,提供更多详细信息。

    【讨论】:

    • cpp文件中可以有模板定义,见this
    • 不要在任何地方包含 .cpp 文件。
    • 这是可能的,但在实例化时它们仍然需要具有所有实现细节。我已经巧妙地编辑了我的答案。
    • 包括cpp 文件我通常不被接受,但在template 实现的特定情况下(鉴于该文件中没有其他内容,就像OP的问题一样)它用于将实现引入翻译单元,使这项工作按预期进行。不确定这是否真的值得投反对票。
    • 不赞成,因为它会在不同的翻译单元中定义两次相同的符号并给出错误。
    猜你喜欢
    • 2012-10-24
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    • 1970-01-01
    相关资源
    最近更新 更多