6
votes

I have a big problem in my C++ Code example. There is something wrong with 'friend' and the 'template'.

Error Messages:
Matrix.h:26:79: warning:

friend declaration 'std::ostream& matrixClass::operator<<(std::ostream&, const matrixClass::Matrix&)' declares a non-template function [-Wnon-template-friend] friend std::ostream &operator<<(std::ostream&, const Matrix &matrix);

Matrix.h:26:79: note:

  (if this is not what you intended, make sure the function template

has already been declared and add <> after the function name here)

Matrix.h:28:77: warning:

  friend declaration 'matrixClass::Matrix<T>*

matrixClass::operator*(const matrixClass::Matrix&, const matrixClass::Matrix&)' declares a non-template function [-Wnon-template-friend] friend Matrix* operator*(const Matrix &m1, const Matrix &m2);

Matrix.cpp:1:0:

C:\Users\Peter\CLionProjects\PK\untitled76\Matrix.h:26:79: warning: friend declaration 'std::ostream& matrixClass::operator<<(std::ostream&, const matrixClass::Matrix&)' declares a non-template function [-Wnon-template-friend] friend std::ostream &operator<<(std::ostream&, const Matrix &matrix);

Matrix.h:26:79: note:

  (if this is not what you intended, make sure the function template

has already been declared and add <> after the function name here)

Matrix.h:28:77: warning:

  friend declaration 'matrixClass::Matrix<T>*

matrixClass::operator*(const matrixClass::Matrix&, const matrixClass::Matrix&)' declares a non-template function [-Wnon-template-friend] friend Matrix* operator*(const Matrix &m1, const Matrix &m2);

CMakeFiles\untitled76.dir/objects.a(main.cpp.obj): In function `main':

main.cpp:8: undefined reference to main.cpp:8: undefined reference to matrixClass::Matrix<int>::Matrix(int)'<br> main.cpp:10: undefined reference tomatrixClass::Matrix::set(int, int, int)'
main.cpp:11: undefined reference to matrixClass::Matrix<int>::set(int, int, int)'<br> main.cpp:12: undefined reference tomatrixClass::Matrix::set(int, int, int)'
main.cpp:13: undefined reference to matrixClass::Matrix<int>::set(int, int, int)'<br> main.cpp:15: undefined reference tomatrixClass::operator<<(std::ostream&, matrixClass::Matrix const&)'
main.cpp:15: undefined reference to matrixClass::operator<<(std::ostream&, matrixClass::Matrix<int> const&)'<br> main.cpp:8: undefined reference tomatrixClass::Matrix::~Matrix()'
main.cpp:8: undefined reference to `matrixClass::Matrix::~Matrix()'

Code: Matrix.h

#ifndef MATRIX_H_
#define MATRIX_H_

#include <iostream>

namespace matrixClass {

    template<class T>
    class Matrix {
    private:
        int dimension;
        T **m;
    public:
        Matrix(int d);

        Matrix(const Matrix &original);

        ~Matrix();

        void set(int x, int y, T value);

        T get(int x, int y) const;

        int getDimension() const;

        friend std::ostream &operator<<(std::ostream&, const Matrix<T> &matrix);

        friend Matrix<T>* operator*(const Matrix<T> &m1, const Matrix<T> &m2);
    };
}

#endif

Matrix.cpp

#include "Matrix.h"

using namespace matrixClass;

template<class T>
Matrix<T>::Matrix(int d)
        : dimension{d}, m{new T *[d]} {
    //m = new T*[d];

    for (int i = 0; i < d; i++) {
        m[i] = new T[d];
    }
}

// COPY-CONSTRUCTOR
template<class T>
Matrix<T>::Matrix(const Matrix &original)
        : dimension{original.dimension},
          m{new T *[original.dimension]} {
    for (int i = 0; i < dimension; i++) {
        *(m + i) = *(original.m + i);
    }
}

// DESTRUCTOR
template<class T>
Matrix<T>::~Matrix() {
    for (int i = 0; i < dimension; i++) {
        delete[] m[i];
    }
    delete[] m;
}

template<class T>
void Matrix<T>::set(int x, int y, T value) {
    m[x][y] = value;
}

template<class T>
T Matrix<T>::get(int x, int y) const {
    return m[x][y];
}

template<class T>
int Matrix<T>::getDimension() const {
    return dimension;
}

template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix) {
    int dimension = matrix.getDimension();

    for(int x = 0; x < dimension; x++) {
        for(int y = 0; y < dimension; y++) {
            output << matrix.get(x, y) << " ";
        }
        return output;
    }
}

template<class T>
Matrix<T>* operator*(const Matrix<T>& m1, const Matrix<T>& m2) {
    int dimension = m1.getDimension();
    Matrix<T>* m = new Matrix<T>(dimension);

    for(int x = 0; x < dimension; x++) {
        for(int y = 0; y < dimension; y++) {
            T value = 0;
            for(int i = 0; i < dimension; i++) {
                value += m1.get(x, i) * m2.get(i, y);
            }
            m->set(x, y, value);
        }
    }
    return m;
}

main.cpp

#include <iostream>
#include "Matrix.h"

using namespace matrixClass;
using namespace std;

int main() {
    Matrix<int> m(2);

    m.set(0, 0, 1);
    m.set(0, 1, 2);
    m.set(1, 0, 3);
    m.set(1, 1, 4);

    cout << m << "*" << endl << m << "=" << endl;

    return 0;
}
2
The template (in your situation Matrix) should have all the declarations and definitions in the header file. Try with that first.bartop
Full error output; it's not minimal; but for saying more than "it's not working", have an upvoteUKMonkey
Make your friend functions also templates and better move the templated member functions in the headerMihayl
*(m + i) = *(original.m + i) will share memory between original and copy. Their destruction will delete twice these memory areas.O'Neil
@O'Neil what do you mean? Do you think it's wrong?Peter Herrlich

2 Answers

5
votes

In friend declaration operator<< refers to a non-template function, while its definition says it's a template function; they don't match.

You can define it inline with the friend declaration (as non-template function):

template<class T>
class Matrix {
    ... ...
    friend std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix) {
        int dimension = matrix.getDimension();

        for(int x = 0; x < dimension; x++) {
            for(int y = 0; y < dimension; y++) {
                output << matrix.get(x, y) << " ";
            }
            return output;
        }
    }
    ... ...
};

Or make the friend declaration referring to the function template:

// class declaration
template<class T>
class Matrix;

// function declaration
template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix);

// class definition
template<class T>
class Matrix {
    ... ...
    friend std::ostream& operator<< <T>(std::ostream& output, const Matrix<T>& matrix);
    ... ...
};

// function definition
template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix) {
    int dimension = matrix.getDimension();

    for(int x = 0; x < dimension; x++) {
        for(int y = 0; y < dimension; y++) {
            output << matrix.get(x, y) << " ";
        }
        return output;
    }
}

And about the undefined reference error, see Why can templates only be implemented in the header file?

0
votes

This answer addresses the problem with your non-member operator<<() and operator*() using Friend Templates, which is slightly different from making an instance of a function template a friend (the second solution presented by @songyuanyao). The difference is that with a Friend Template, all instances of the template function are friends of the class Matrix<>. In this case, I don't think there is any practical difference, but in others, there may be. Therefore, I figured I'd present it anyway.

I think about it like this. Both operators are non-member functions which means they are independent from the class Matrix<>, so think of their prototypes independently:

template<class T>
std::ostream& operator<<(std::ostream& output, const Matrix<T>& matrix);

template<class T>
Matrix<T>* operator*(const Matrix<T>& m1, const Matrix<T>& m2);

Now, replace T with U because to make all instances of them friends of Matrix<>, their prototype needs to be included in the class definition for Matrix<> which is also a template, and it is already using T as its template parameter.

template<class U>
std::ostream& operator<<(std::ostream& output, const Matrix<U>& matrix);

template<class U>
Matrix<U>* operator*(const Matrix<U>& m1, const Matrix<U>& m2);

Now, you can make them friends of Matrix<>. All you need is the appropriate syntax:

template<class T>
class Matrix
{
    ...
    template<class U>
    friend std::ostream &operator<<(std::ostream&, const Matrix<U> &matrix);

    template<class U>
    friend Matrix<U>* operator*(const Matrix<U> &m1, const Matrix<U> &m2);
};    

Finally, they all need to be within the matrixClass namespace, and their declarations and definitions need to be in the right order so that everyone know the others exists:

namespace matrixClass {

// BEGIN Forward declarations

template<class T>
class Matrix;

template<class U>
std::ostream &operator<<(std::ostream&, const Matrix<U> &matrix);

template<class U>
Matrix<U>* operator*(const Matrix<U> &m1, const Matrix<U> &m2);

// END Forward declarations

template<class T>
class Matrix
{
    ...
    template<class U>
    friend std::ostream &operator<<(std::ostream&, const Matrix<U> &matrix);

    template<class U>
    friend Matrix<U>* operator*(const Matrix<U> &m1, const Matrix<U> &m2);
};

...

template<class U>
std::ostream& operator<<(std::ostream& output, const Matrix<U>& matrix)
{
    ...   // your implementation goes here
}

template<class U>
Matrix<U>* operator*(const Matrix<U>& m1, const Matrix<U>& m2)
{
    ...   // your implementation goes here
}

}   // end of namespace matrixClass

All this template code should be in the header file, Matrix.h, as it has already been mentioned.

Also, I think that you should change the prototype of operator*() to return Matrix<U> instead of Matrix<U>*. It will behave more like the normal operator*(), and it'll enable you to do things like: Matrix<int> m = m1*m2; or m = m1*m2*m3;. Additionally, the users of your class won't have to worry about deleting the memory allocated by operator*() or the performance cost of the dynamic allocation. Just create a local Matrix<U> variable in operator*() and return it by value; let Return Value Optimization (RVO) take care of the rest.