Вопрос об объявлении функции-друга в шаблонном классе (C ++) - PullRequest
2 голосов
/ 12 апреля 2020

Я пишу свое задание, которое реализует некоторые функции матрицы. Вот упрощенная версия, чтобы исправить проблему.

#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;
}

Наш учитель сказал, что мы должны сделать «DisplayMatrix» глобальной функцией, поэтому она должна быть функцией-другом класса CMyMatrix (если я не не хочу писать больше функций). Но есть исключение, подобное этому.
Код: LNK2019; Описание: неразрешенный внешний символ "void _cdecl DisplayMatrix (класс CMyMatrix)" (? DisplayMatrix @@ YAXV? $CMyMatrix@H@@@Z), указанный в функции _main; Линия 1; Файл: CMyMatrix.obj

Я заметил, что «DisplayMatrix» в классе CMyMatrix не меняет цвет в моей IDE, поэтому я думаю, что могут быть некоторые проблемы. Но я не знаю, как это решить. Буду признателен, если кто-нибудь сможет мне помочь.

Ответы [ 2 ]

4 голосов
/ 12 апреля 2020

Объявление friend объявляет не шаблонную функцию, которая не соответствует определению шаблона функции в глобальной области видимости.

Вы можете

// 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;
            }
        }
    }
}

Или объявить шаблон friend как

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;
            }
        }
    }
}
0 голосов
/ 12 апреля 2020

Я хотел бы представить ответ, который на самом деле не является решением. Смысл этого не решения состоит в том, чтобы объяснить, почему решение работает (я допускаю, что этот ответ будет стоить мало без ответа, предоставленного songyuanyao.) Это может помочь прояснить вещи для будущих читателей, особенно если некоторые комментарии исчезают.

Для начала приведем упрощенную настройку, в которой я удалил некоторые посторонние вопросы (c .f. минимальный, воспроизводимый пример ):

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;
}

Проблема в том, что friend объявлен как (не шаблонная) функция, а определение DisplayMatrix является шаблоном функции. Они не совпадают, поэтому для идентификатора DisplayMatrix у компилятора остается два значения. Когда выясняется, какое значение использовать для строки DisplayMatrix(matrix);, компилятор предпочитает не шаблон (который объявлен, но никогда не определен).

Таким образом, ответ заключается в том, чтобы согласовать ваше объявление и определение. Один из способов сделать это - скорее всего, не решение, которое кто-то ищет, а способ порадовать компилятор - это изменить определение, чтобы оно больше не было шаблоном.

void DisplayMatrix(CMyMatrix<int>) {
}

С этим изменением код компилируется. Ну, это соответствует требованиям, если вы используете int в качестве параметра шаблона. Так что на самом деле не решение, а демонстрация того, что декларация friend фактически объявляет. Реальное решение состоит в том, чтобы сохранить (шаблонное) определение DisplayMatrix и настроить объявление friend, как в принятом ответе.


Может быть интересно отметить, что g cc ( но явно не лязг?) есть предупреждение, применимое к этой ситуации.

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) 
...