C++模板类ostream<<运算符分离编译时未定义引用错误求助
模板类分离编译的链接错误求助
将模板类声明放在array.h、实现放在array.cpp时,编译ex2.cpp出现如下链接错误,但把所有代码放在同一个头文件或.cpp文件中则正常:
/usr/bin/ld: ex2.o: in function `main': ex2.cpp:(.text+0xd0): undefined reference to `std::ostream& operator<< <int>(std::ostream&, array<int> const&)' /usr/bin/ld: ex2.cpp:(.text+0xe6): undefined reference to `std::ostream& operator<< <float>(std::ostream&, array<float> const&)' /usr/bin/ld: ex2.cpp:(.text+0xfc): undefined reference to `std::ostream& operator<< <double>(std::ostream&, array<double> const&)' collect2: error: ld returned 1 exit status
试过相关解决方案但无效,想问有没有解决办法?是不是只能把array.cpp的代码放进头文件或者通过头文件包含?
以下是各文件代码:
array.h
#ifndef ARRAY_H #define ARRAY_H #include <ostream> template <typename T> class array { public: array (int size); array (const array<T> &ob); ~array (); void setElemet (T value, int pos); int getElement (int pos) const; int getSize() const; void arrayInit (); int operator[](int pos); array<T> &operator=(const array<T> &right); array<T> &operator+=(int right); template <typename U> friend std::ostream &operator<<(std::ostream &left, const array<U> &right); private: T *arr; int size; }; #endif
array.cpp
#include <iostream> #include "array.h" #include <random> using std::cout, std::endl; template class array<int>; template class array<float>; template class array<double>; template <typename T> array<T>::array(int size) { this -> size = size; if(this -> size <= 0) { if(this -> size == 0) this -> size = 1; else this -> size *= -1; cout << "Array size corrected, current size = " << this -> size << "." << endl; } try { arr = new T[this -> size]; } catch (const std::exception& ex) { cout << "Memory allocation failed, reason: " << ex.what() << endl; std::terminate(); } arrayInit(); } template <typename T> array<T>::array(const array<T> &ob) { this -> size = ob.size; try { arr = new T[this -> size]; } catch (const std::exception& ex) { cout << "Memory allocation failed, reason: " << ex.what() << endl; std::terminate(); } for (int i = 0; i < size; i++) { arr[i] = ob.arr[i]; } } template <typename T> array<T>::~array () { delete[] arr; } template <typename T> void array<T>::setElemet (T value, int pos) { if(pos < 0 || pos >= size) { cout << "Invalid position, array ranges from 0 to " << size - 1 << "." << endl; } else { arr[pos] = value; } } template <typename T> int array<T>::getElement (int pos) const{ if(pos < 0 || pos >= size) { cout << "Invalid position, array ranges from 0 to " << size - 1 << "." << endl; return -1; } else { return arr[pos]; } } template <typename T> int array<T>::getSize() const { return size; } template <typename T> void array<T>::arrayInit (){ std::random_device rd; if(std::is_same<T, int>::value) { std::uniform_int_distribution <int> dist (-10000, 10000); for (int i = 0; i < size; i++) { arr[i] = dist(rd); } } else if (std::is_same<T, float>::value) { std::uniform_real_distribution <float> dist (-10000, 10000); for (int i = 0; i < size; i++) { arr[i] = dist(rd); } } else if (std::is_same<T, double>::value) { std::uniform_real_distribution <double> dist (-10000, 10000); for (int i = 0; i < size; i++) { arr[i] = dist(rd); } } } template <typename T> int array<T>::operator[](int pos) { try { if (pos < 0 || pos >= size) throw pos; return arr[pos]; } catch (int ex) { cout << "Tried to input out of array size. Array ranges from 0 to " << size - 1 << ". Position tried to input: "; return pos; } } template <typename T> array<T> &array<T>::operator=(const array<T> &right) { if (this == &right) return *this; for (int i = 0; i < size; i++) { arr[i] = right.arr[i]; } return *this; } template <typename T> array<T> &array<T>::operator+=(int right) { if (right <= 0) { cout << "Can't expand array with negative or zero input." << endl; return *this; } int oldSize = size; array<T> temp(oldSize); size += right; temp = *this; delete [] arr; try { arr = new T[size]; } catch(const std::exception& ex) { cout << "Memory allocation failed, reason: " << ex.what() << endl; std::terminate(); } for (int i = 0; i < oldSize; i++) { arr[i] = temp[i]; } std::random_device rd; if(std::is_same<T, int>::value) { std::uniform_int_distribution <int> dist (-10000, 10000); for (int i = 0; i < size; i++) { arr[i] = dist(rd); } } else if (std::is_same<T, float>::value) { std::uniform_real_distribution <float> dist (-10000, 10000); for (int i = 0; i < size; i++) { arr[i] = dist(rd); } } else if (std::is_same<T, double>::value) { std::uniform_real_distribution <double> dist (-10000, 10000); for (int i = 0; i < size; i++) { arr[i] = dist(rd); } } return *this; } template <typename T> std::ostream &operator<<(std::ostream &left, const array<T> &right) { if(right.size <= 0) { left << "Array size invalid." << "endl"; } else { for(int i = 0; i < right.size; i++) { left << "Array element " << i << " has a value = " << right.arr[i] << "." << endl; } } return left << endl; }
ex2.cpp
#include <iostream> #include "array.h" using std::cout, std::cin, std::endl; int main() { array<int> arr1(3); array<float> arr2(3); array<double> arr3(3); cout << arr1; cout << arr2; cout << arr3; return 0; }
解决方案
问题核心是友元operator<<模板没有被显式实例化。你已经显式实例化了array<int>、array<float>、array<double>类,但友元的operator<<是独立的模板函数,不会随着类的实例化自动生成对应类型的实现,导致链接器找不到对应版本。
方法1:显式实例化友元函数
在array.cpp中,类的显式实例化代码之后,添加以下代码:
template std::ostream& operator<< <int>(std::ostream&, const array<int>&); template std::ostream& operator<< <float>(std::ostream&, const array<float>&); template std::ostream& operator<< <double>(std::ostream&, const array<double>&);
这样链接器就能找到这三个类型对应的operator<<实现。
方法2:将实现移到头文件(工业界常用方案)
模板本质是“代码生成器”,编译器需要在使用模板的地方看到完整的实现才能生成对应类型的代码。最常用的做法是把模板类的所有实现(包括成员函数和友元函数)都放在头文件里,或者在array.h的末尾添加#include "array.cpp",这样编译ex2.cpp时就能获取完整的模板实现,自动生成所需代码。
方法3:模板特化(扩展性差)
如果只需要支持int、float、double三种类型,可以直接为这三种类型特化operator<<,但这种方式新增类型时需要重复编写代码,扩展性较差。
内容的提问来源于stack exchange,提问作者Agent 47
相关产品推荐
相关产品推荐

