C++类模板中声明友元函数

注意: 由于鄙人水平有限, 该文章中可能会有错误之处, 还恳请读者批评指正!

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提示了这个友元函数声明是一个非模版函数, 而且即使编译通过也无法链接

下面介绍几种正确的写法:

  1. 需要模板参数的非模板函数, 定义写在类内
    代码如下:

    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;
        }
    };
  2. 需要模板参数的模板函数,自带模板参数
    代码如下:

    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;
    }
  3. 需要模板参数的模板函数,使用类模板参数实例化
    代码如下:

    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

标题: C++类模板中声明友元函数
作者: QingChenW
链接: https://dawncraft.cc/2021/04/117/
本文遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 许可
禁止商用, 非商业转载请注明作者及来源!
上一篇
下一篇
隐藏