C++类模板未定义对函数的引用。

9

当我在主函数中调用模板类"add"和"greater"中的两个函数时,我一直得到未定义的引用错误。

所以,我有: number.h

#ifndef NUMBER_H
#define NUMBER_H

template <class T>
class number {
public:
    T x;
    T y;

    number (int a, int b){
        x=a; y=b;}
    int add (T&);
    T greater ();
};

#endif

number.cpp

#include "number.h"

template <class T>
int number<T>::add (T& rezAdd){
    rezAdd = x+y;
    return 1;
}

template <class T>
T number<T>::greater (){
        return x>y? x : y;
}

我的主文件是:resolver.cpp

#include <stdio.h>
#include <stdlib.h>
#include "number.h"

int main (int argc, char **argv) {
    int aux;
    number<int> c(3,5);

    c.add(aux);
    printf ("number added [%d]\n", c.add(aux));
    printf ("greater number: [%d]\n", c.greater());

    return 0;
}

我往常遇到的错误是:
g++ -Wall -o tema1 resolver.cpp number.cpp
/tmp/ccX483J4.o: In function `main':
resolver.cpp:(.text+0x34): undefined reference to `number<int>::add(int&)'
resolver.cpp:(.text+0x47): undefined reference to `number<int>::add(int&)'
resolver.cpp:(.text+0x64): undefined reference to `number<int>::greater()'
collect2: ld returned 1 exit status
make: *** [all] Error 1

事先感谢您的帮助!

3个回答

6
我更喜欢将所有的函数放在.cpp文件中,不管它们是模板函数还是常规函数。有一种方法可以通过一些基本的#ifndef魔法来实现这一点。你可以这样做: main.cpp
#include "myclass.hpp"

int main()
{
  // ...
}

myclass.hpp

#ifndef MYCLASS
#define MYCLASS

template<class T>
class MyClass
{
  T val;
public:
  MyClass(T val_);
}

#define MYCLASS_FUNCTIONS
#include "myclass.cpp"

#endif

myclass.cpp

#ifndef MYCLASS_FUNCTIONS
#include "myclass.hpp"

// regular functions:
// ...

#else

// template functions:
template<class T>
MyClass<T>::MyClass(T val_)
    :val(val_)
{}

// ...
#endif

这是预编译器的处理过程。我们有两个 .cpp 文件。
1. 当我们编译 main.cpp 时: - 包含 myclass.hpp - 检查 MYCLASS 是否未定义,它是未定义的 - 定义 MYCLASS - 提供编译器生成类(来自模板类)的定义 - 包含 myclass.cpp - 定义 MYCLASS_FUNCTIONS - 检查 MYCLASS_FUNCTIONS 是否已定义,已经定义了 - 提供编译器生成函数(来自模板函数)的定义
2. 当我们编译 myclass.cpp 时: - 检查 MYCLASS_FUNCTIONS 是否已定义,它没有被定义 - 包含 myclass.hpp - 检查 MYCLASS 是否未定义,它是未定义的 - 定义 MYCLASS - 提供类的定义 - 包含 myclass.cpp - 再次包含 myclass.hpp - 这次 MYCLASS 被定义了,所以在其中什么也不做,返回到 myclass.cpp - 检查 MYCLASS_FUNCTIONS 是否已定义,已经定义了 - 提供生成函数(来自模板函数)的定义 - 退出两次包含 - 将所有常规函数传递给编译器

最佳解决方案。我正好在寻找这个,花了一个月才找到它。(“迟做总比不做好”)。 - Shravan40

5
将函数模板addgreater的定义移到你的number.h中。
请记住,addgreater不是函数,它们是函数模板。要创建实际函数,编译器必须为特定类型(例如int)实例化模板,只有在发现需要实例时才能这样做,并且只有在访问模板定义的地方才能这样做。
当你编译number.cpp时,编译器可以访问模板的定义,但它没有看到需要特定实例的代码(例如number<int>),因此它不会生成实例。
当你编译resolver.cpp时,编译器看到它需要为int类型实例化那些模板,但它不能这样做,因为它没有它们的定义。所以它生成了“外部引用”,基本上是告诉链接器在某个其他对象文件中查找那些函数的备注。
结果是函数模板在任何一个对象文件中都没有被实例化 - 一个因为编译器不知道它应该,另一个因为它无法 - 因此当链接器寻找它们(以解析这些外部引用)时,它找不到它们。这就是为什么你会得到错误的原因。
将模板函数定义移到头文件中使它们在编译main.cpp时对编译器可见,因此它能够为int类型实例化这些函数。正因为这个原因,函数模板通常需要在头文件中定义,而不是.cpp文件中定义。

非常有用。感谢帮助! - MihaiGrad

5
你的类名错误了。你的类被称为 cai,而所有函数都属于一个名为 number 的类: http://ideone.com/ZayX0c 还有一件事... 你不能在 .cpp 文件中使用模板。模板函数/定义应与类声明一起放在头文件中。这就是你未定义函数错误的原因。非模板函数放在 .cpp 文件中。
#include <cstdio>
#include <cstdlib>

template <class T>
class number {
public:
    T x;
    T y;

    number (int a, int b){
        x=a; y=b;}
    int add (T&);
    T greater ();
};

template <class T>
int number<T>::add (T& rezAdd){
    rezAdd = x+y;
    return 1;
}

template <class T>
T number<T>::greater (){
        return x>y? x : y;
}


int main (int argc, char **argv) {
    int aux;
    number<int> c(3,5);

    c.add(aux);
    printf ("number added [%d]\n", c.add(aux));
    printf ("greater number: [%d]\n", c.greater());

    return 0;
}

我编辑错了,非常抱歉。 - MihaiGrad
我编辑了答案。将您的模板实现放在头文件(.h)中。非模板实现放在主体(.cpp)中。 - Brandon
有人建议在主函数中使用“number<int> c = number<int>(3, 5);”修改“number<int> c(3,5);”,但我仍然遇到了之前提到的错误。 - MihaiGrad
在这种情况下没有区别。你所需要做的就是将所有的template函数移动到头文件中,将所有非模板函数移动到实现文件中。 - Brandon
现在可以运行了!非常感谢您的帮助。 - MihaiGrad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接