注意: 由于鄙人水平有限, 该文章中可能会有错误之处, 还恳请读者批评指正!
C++的一次作业要求我们写一个使用模板的安全数组类, 要求具有越界检查, 重载[]和<<等运算符, 在重载输出运算符时出现了一些问题, 记录如下
测试代码如下(简化后):
#include <iostream> using std::cin, std::cout, std::endl, std::ostream; template <typename T> class MyArray; // 安全数组类的声明 int main() { MyArray<int> a1; cout << a1 << endl; return 0; }
大部分同学对安全数组类的定义都是这样的
template <typename T> class MyArray { int size; T *elements; public: MyArray(int size): size(size), elements(new T[size]) {} MyArray(): MyArray(0) {} ~MyArray() { delete[] elements; } // 省略其它成员函数... friend ostream& operator<<(ostream &out, const MyArray<T> &a); }; template <typename T> ostream& operator<<(ostream &out, const MyArray<T> &a) { out << "Stub!"; return out; }
代码看似没有问题, 但编译后会报warning并且无法链接
warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const MyArray<T>&)’ declares a non-template function [-Wnon-template-friend] 35 | friend ostream& operator<<(ostream &out, const MyArray<T> &a); | ^ undefined reference to `operator<<(std::ostream&, MyArray<int> const&)'
问题出在类中声明的友元函数是一个需要模板参数的非模板函数, 但类外定义的是需要模板参数的模板函数, 二者无法匹配
所以warning提示了这个友元函数声明是一个非模版函数, 而且即使编译通过也无法链接
下面介绍几种正确的写法:
- 需要模板参数的非模板函数, 定义写在类内
代码如下:template <typename T> class MyArray { int size; T *elements; public: MyArray(int size): size(size), elements(new T[size]) {} MyArray(): MyArray(0) {} ~MyArray() { delete[] elements; } friend ostream& operator<<(ostream &out, const MyArray<T> &a) { // 也可以省略<T>让编译器自动推断 out << "Stub!"; return out; } };
- 需要模板参数的模板函数,自带模板参数
代码如下:template <typename T> class MyArray { int size; T *elements; public: MyArray(int size): size(size), elements(new T[size]) {} MyArray(): MyArray(0) {} ~MyArray() { delete[] elements; } template <typename T2> friend ostream& operator<<(ostream &out, const MyArray<T2> &a); // 声明了一个模板函数作为友元, 所以可以 }; template <typename T> ostream& operator<<(ostream &out, const MyArray<T> &a) { out << "Stub!"; return out; }
- 需要模板参数的模板函数,使用类模板参数实例化
代码如下:template <typename T> class MyArray; template <typename T> ostream& operator<<(ostream &out, const MyArray<T> &a); template <typename T> class MyArray { int size; T *elements; public: MyArray(int size): size(size), elements(new T[size]) {} MyArray(): MyArray(0) {} ~MyArray() { delete[] elements; } friend ostream& operator<<<T>(ostream &out, const MyArray<T> &a); // T可以省略, 但是<>不可以省略, 因为如果不写<T>就成非模版函数了 // 写上后是显式指明模板参数从而实例化T所对应的函数 }; template <typename T> ostream& operator<<(ostream &out, const MyArray<T> &a) { out << "Stub!"; return out; }
如果还没有完全理解也可以看看知乎上的这篇回答: https://www.zhihu.com/question/44130347