简体   繁体   中英

A question about declaring a friend template function in a template class(C++)

I'm writing my assignment that implements some functions of matrix. Here is a simplified version in order to fix on the problem.

#include <iostream>
using namespace std;

template<class T>
class CMyMatrix{
private:
    int row;
    int col;
    T** elements;
public:
    CMyMatrix(int row, int col);
    void SetMatrix(T* data, int row, int col);
    friend void DisplayMatrix(CMyMatrix<T> matrix);
};

template<class T>
CMyMatrix<T>::CMyMatrix(int row,int col) {
    this->row = row;
    this->col = col;
    this->elements = new T* [row];
    for (int i = 0;i < row;i++) {
        this->elements[i] = new T[col];
    }
}

template<class T>
void CMyMatrix<T>::SetMatrix(T* data, int row, int col) {
    this->row = row;
    this->col = col;
    if (elements != 0) delete[]elements;
    this->elements = new T* [row];
    for (int i = 0;i < row;i++) {
        this->elements[i] = new T[col];
    }
    for (int i = 0;i < row * col;i++) {
        elements[i / col][i % col] = data[i];
    }
}


template<class T>
void DisplayMatrix(CMyMatrix<T> matrix) {
    for (int i = 0;i < matrix.row;i++) {
        for (int j = 0;j < matrix.col;j++) {
            cout << matrix.elements[i][j] << " ";
            if (j == matrix.col - 1) {
                cout << endl;
            }
        }
    }
}

int main(){
    CMyMatrix<int> matrix(2, 3); 
    int a[6] = {1, 2, 3, 4, 5, 6};
    matrix.SetMatrix(a, 2, 3);
    DisplayMatrix(matrix);
    return 0;
}

Our teacher said we have to make "DisplayMatrix" a global function so it has to be a friend function of class CMyMatrix(If I don't want to write more functions). But there is an exception like this.
Code: LNK2019; Description: unresolved external symbol "void _cdecl DisplayMatrix(class CMyMatrix)"(?DisplayMatrix@@YAXV?$CMyMatrix@H@@@Z) referenced in function _main; Line 1; File:CMyMatrix.obj

I notice that the "DisplayMatrix" in class CMyMatrix don't change color in my IDE, so I think there might be some problems. But I don't know how to solve it. I would appreciate it if someone could help me.

The friend declaration declares a non-template function, which doesn't match the function template's definition in the global scope.

You could

// forward declaration
template<class T>
class CMyMatrix;

// declaration
template<class T>
void DisplayMatrix(CMyMatrix<T> matrix);

template<class T>
class CMyMatrix{
private:
    int row;
    int col;
    T** elements;
public:
    CMyMatrix(int row, int col);
    void SetMatrix(T* data, int row, int col);
    // friend declaration; refering to the function template
    friend void DisplayMatrix<T>(CMyMatrix<T> matrix);
    //                       ^^^
};

...

// definition
template<class T>
void DisplayMatrix(CMyMatrix<T> matrix) {
    for (int i = 0;i < matrix.row;i++) {
        for (int j = 0;j < matrix.col;j++) {
            cout << matrix.elements[i][j] << " ";
            if (j == matrix.col - 1) {
                cout << endl;
            }
        }
    }
}

Or declare template friend as

template<class T>
class CMyMatrix{
private:
    int row;
    int col;
    T** elements;
public:
    CMyMatrix(int row, int col);
    void SetMatrix(T* data, int row, int col);
    // declares template friend;
    // note that all the instantiations of DisplayMatrix become friend
    template <class X>
    friend void DisplayMatrix(CMyMatrix<X> matrix);
};

...

template<class T>
void DisplayMatrix(CMyMatrix<T> matrix) {
    for (int i = 0;i < matrix.row;i++) {
        for (int j = 0;j < matrix.col;j++) {
            cout << matrix.elements[i][j] << " ";
            if (j == matrix.col - 1) {
                cout << endl;
            }
        }
    }
}

I would like to present an answer that is not really a solution. The point of this non-solution is to expand upon why the solution works (I admit that this answer would be worth little without the answer provided by songyuanyao.) This might help clarify things for future readers, especially if certain comments ever disappear.

To start, here is a simplified setup, where I've removed some things extraneous to the question (cf minimal, reproducible example ):

template<class T>
class CMyMatrix{
    friend void DisplayMatrix(CMyMatrix<T>);
};

template<class T>
void DisplayMatrix(CMyMatrix<T>) {
}

int main(){
    CMyMatrix<int> matrix;
    DisplayMatrix(matrix);
    return 0;
}

The problem is that the friend is declared as a (non-template) function, while the definition of DisplayMatrix is a function template. These do not match, so the compiler is left with two meanings for the identifier DisplayMatrix . When figuring out which meaning to use for the line DisplayMatrix(matrix);, the compiler prefers the non-template (which is declared, but never defined).

So the answer is to make your declaration and definition agree. One way to do this – likely not the solution anyone is looking for, but a way to make the compiler happy – is to change the definition to no longer be a template.

void DisplayMatrix(CMyMatrix<int>) {
}

With this change, the code compiles. Well, it complies provided you stick to int as your template parameter. So not really a solution, but a demonstration of what the friend declaration actually declares. The real solution is to keep the (template) definition of DisplayMatrix and adjust the friend declaration, as in the accepted answer.


It might be interesting to note that gcc (but apparently not clang?) has a warning applicable to this situation.

 warning: friend declaration 'void DisplayMatrix(CMyMatrix<T>)' declares a non-template function [-Wnon-template-friend] friend void DisplayMatrix(CMyMatrix<T> matrix); ^ note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM