You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

C++模板类跨文件调用遇undefined reference错误排查

问题解答:模板类的Undefined Reference错误

编辑说明

感谢用户Richard Critten的提示,这个问题的核心是对模板(Templates)工作机制的理解偏差。模板函数的实现必须放在类的头文件中,否则编译器无法为任意类型生成对应的模板实例代码,最终导致链接时出现undefined reference错误。


问题背景

我在编写一个名为Tensor的模板类,用来存储任意类型T的元素,构造函数接收多个参数。但当我在其他文件中创建Tensor对象时,总是触发undefined reference错误。我已经确认头文件包含正确,甚至写了一个无关的函数test_bois(),它能被其他文件正常调用,说明库的编译和链接本身是没问题的。

更奇怪的是:如果把main()函数放在tensor.cpp里,取消注释后能正常运行(代码和main.cpp里的完全一致),但放在main.cpp里就报错。我用CMake把tensor.htensor.cpp编译成库,再链接到可执行文件,怀疑问题出在构建环节,但不确定具体原因。

最小可复现示例(MWE)

CMake脚本片段

add_library (libTensor tensor.h tensor.cpp)
add_executable(Main.exe test_main.cpp)
target_link_libraries(Main.exe libTensor)
#add_executable (Tensor.exe tensor.cpp)
#target_link_libraries (Tensor.exe Tensor)

头文件tensor.h

#ifndef TENSOR_H
#define TENSOR_H
#include <vector>

template<typename T>
class Tensor {
    int width, height;
public:
    Tensor(int n, int m) { height = n; width = m; };
    Tensor(int n, int m, std::vector<T> elems);
};

int test_bois();

#endif

实现文件tensor.cpp

#include <iostream>
#include "tensor.h"

template<typename T>
Tensor<T>::Tensor(int n, int m) {
    height = n;
    width = m;
}

template<typename T>
Tensor<T>::Tensor(int n, int m, std::vector<T> elems) {
    height = n + 100;
    width = m + 100;
}

int test_bois() {
    return 42;
}

// int main()
// {
//     std::vector<int> elems(10);
//     int height = 2;
//     int width = 2;
//     Tensor<int> *my_obj_zero = new Tensor<int>(height, width);
//     Tensor<int> *my_obj_one= new Tensor<int>(height, width, elems);
//     std::cout << my_obj_zero->shape() << std::endl;
//     std::cout << my_obj_one->shape() << std::endl;
//     std::cout << "Hello World!!" << std::endl;
//     return 0;
// }

主文件main.cpp

#include <iostream>
#include "tensor.h"

int main() {
    std::cout << test_bois() << std::endl; // 注释掉下面的代码就能正常运行
    std::vector<int> elems(10);
    int height = 2;
    int width = 2;
    Tensor<int> *my_obj_zero = new Tensor<int>(height, width);
    Tensor<int> *my_obj_one= new Tensor<int>(height, width, elems);
    std::cout << my_obj_zero->shape() << std::endl;
    std::cout << my_obj_one->shape() << std::endl;
    std::cout << "Hello World!!" << std::endl;
    return 0;
}

问题原因与解决方案

核心原因

模板不是真正的可执行代码,而是代码生成的蓝图。当编译器处理模板类时,只有在看到具体的类型实例(比如Tensor<int>)时,才会生成对应的类和成员函数的机器码。

如果把模板的实现放在.cpp文件里,编译tensor.cpp的时候,编译器不知道你在main.cpp里会用到Tensor<int>,所以不会生成Tensor<int>的构造函数代码。等到链接阶段,main.cpp里调用Tensor<int>的构造函数时,找不到对应的机器码,就会报undefined reference错误。

而把main()放在tensor.cpp里时,编译器在同一个文件里看到了Tensor<int>的使用,就会生成对应的代码,所以能正常运行。

解决方案

把模板类的所有成员函数实现移到头文件中,可以有两种方式:

  1. 直接在类定义内实现
    修改tensor.h
#ifndef TENSOR_H
#define TENSOR_H
#include <vector>

template<typename T>
class Tensor {
    int width, height;
public:
    Tensor(int n, int m) { 
        height = n; 
        width = m; 
    };
    Tensor(int n, int m, std::vector<T> elems) {
        height = n + 100;
        width = m + 100;
    }
};

int test_bois();

#endif
  1. 在头文件中类定义外实现(但仍需在头文件里)
    修改tensor.h
#ifndef TENSOR_H
#define TENSOR_H
#include <vector>

template<typename T>
class Tensor {
    int width, height;
public:
    Tensor(int n, int m);
    Tensor(int n, int m, std::vector<T> elems);
};

// 实现放在头文件里
template<typename T>
Tensor<T>::Tensor(int n, int m) {
    height = n;
    width = m;
}

template<typename T>
Tensor<T>::Tensor(int n, int m, std::vector<T> elems) {
    height = n + 100;
    width = m + 100;
}

int test_bois();

#endif

然后删除tensor.cpp里的模板函数实现(保留test_bois()即可),重新编译就能解决问题了。


内容的提问来源于stack exchange,提问作者Prunus Persica

火山引擎 最新活动