最近笔者在Linux环境下学习C++,用g++编译时遇到一个错误。笔者在crc.hpp文件中给出了类的声明,并在crc.cpp文件中给出了类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* crc.hpp */
template <typename Type>
class CRC{
private:
Type POLY;
Type INIT;
Type XOROUT;
bool REFIN;
bool REFOUT;
template <typename PrivType>
PrivType Ref(PrivType Data);
public:
CRC(Type POLY, Type INIT, Type XOROUT, bool REFIN, bool REFOUT);
Type Encode(const uint8_t *Addr, uint32_t Length);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* crc.cpp */
template <typename Type>
CRC<Type>::CRC(Type SetPOLY, Type SetINIT, Type SetXOROUT, bool SetREFIN, bool SetREFOUT){
/* Code */
}

template <typename Type>
template <typename PrivType>
PrivType CRC<Type>::Ref(PrivType Data){
/* Code */
}

template <typename Type>
Type CRC<Type>::Encode(const uint8_t *Addr, uint32_t Length){
/* Code */
}

然后笔者在main.hpp中引用crc.hpp,并在main.cpp中实例化了一个对象

1
CRC<uint16_t> CRC16(0x8005, 0x0000, 0x0000, true, true);

测试时用到

1
2
3
4
5
6
7
void Thread_1000ms(void){
uint16_t crc16;
/* ... */
crc16 = CRC16.Encode((uint8_t*)"12345", 5);
cout << crc16 << "\n" << endl;
/* ... */
}

编译上述代码时一直报错:

1
2
3
/usr/bin/ld: /tmp/cc1W785D.o: in function `Thread_1000ms()':
main.cpp:(.text+0xb0): undefined reference to `CRC<unsigned short>::Encode(unsigned char const*, unsigned int)'
collect2: error: ld returned 1 exit status

查找了一下解决方案,都说声明/实现无法分离,这种回答令人感到不可思议,怎么可能无法分离?经过数日的查找,终于找到解决方案:模板类声明/定义分离时,需要在*.cpp文件中对模板类进行特化处理。具体的说,就是在上述*.cpp文件开头加入一句

1
template class CRC<uint16_t>;

终于编译成功。并且经过测试笔者发现,这样还有一个额外的技术效果:如果在*.cpp文件中对模板类进行特化时只写

1
2
3
template class CRC<uint8_t>;
template class CRC<uint16_t>;
template class CRC<uint32_t>;

则在其他地方实例化时只能实例化uint8_tuint16_tuint32_t三种类型的对象,如果尝试实例化CRC<double> CRC16(0x8005, 0x0000, 0x0000, true, true);,编译器会报undefined reference to错误,这样便限制了模板类的输入类型。

参考文献

[1] 小强的机器人工坊. C++模板类的申明和定义分离的实现方法[DB/OL]. https://blog.csdn.net/l1216766050/article/details/112107682, 2021-01-02/2021-08-28

[2] huanghxyz. C++泛型模板函数编程中“undefined reference to”错误[DB/OL]. https://blog.csdn.net/huanghxyz/article/details/85280144, 2018-12-27/2021-08-28