I've been stuck on this code for a while and can't get it to compile, what exactly am I doing wrong? If there are bugs present when it compiles, please ignore them as I can fix that on my own. As of right now I'm just trying to get it to run. Thank you in advance.
#include <iostream>
#include <string.h>
//template <class t> class Matrix; //possible way of fixing the friend function.
using namespace std;
template<class T, size_t NROWS, size_t NCOLS>
std::ostream & operator<<(std::ostream &os, const Matrix<T,NROWS, NCOLS> &matrix);
template<class T, size_t NROWS = 1, size_t NCOLS = 1>
class Matrix{
public:
Matrix(){}
friend std::ostream &operator<< <>(std::ostream&os,const Matrix<T, NROWS, NCOLS> &matrix);
private:
T container[NROWS][NCOLS];
};
template<class T,size_t NROWS, size_t NCOLS>
std::ostream &operator<<(std::ostream &os,const Matrix<T,NROWS,NCOLS>&matrix){
for(size_t i=0;i<NROWS;++i){
for(size_t j=0;j<NCOLS;++j){
os <<matrix.container[i][j]<<" ";
}
os <<std::endl;
}
os <<std::endl;
}
int main(){
Matrix<float, 10, 5> mat;
cout << mat;
return 0;
}
The errors from the IDE I use are as follows:
main.cpp:8:51: error: no template named 'Matrix'
std::ostream & operator<<(std::ostream &os, const Matrix &matrix);
main.cpp:15:24: error: no function template matches function template specialization 'operator<<' friend std::ostream &operator<< <>(std::ostream&os,const Matrix &matrix);
main.cpp:35:32: note: in instantiation of template class 'Matrix' requested here Matrix mat;
If you uncomment out line 4, and change it as follows, the code you have compiles:
template <class t, size_t, size_t> class Matrix; //possible way of fixing the friend function.
It seems your problem is that the forward-declared Matrix template parameters don't match the Matrix definition that comes later.
Also, although the code will compile after this fix, there is still a warning which you probably also want to fix:
In function 'std::ostream& operator<<(std::ostream&, const Matrix<T, NROWS, NCOLS>&)':
31:1: warning: no return statement in function returning non-void [-Wreturn-type]
#include <cstddef>
#include <iostream>
template<typename, std::size_t, std::size_t> class Matrix;
template<typename T, std::size_t NROWS, std::size_t NCOLS>
std::ostream& operator<<(std::ostream &os, Matrix<T, NROWS, NCOLS> const &matrix)
{
for (std::size_t row{}; row < NROWS; ++row, os.put('\n'))
for (std::size_t col{}; col < NCOLS; ++col)
os << matrix.container[row][col] << ' ';
return os.put('\n');
}
template<typename T, std::size_t NROWS = 1, std::size_t NCOLS = 1>
class Matrix {
T container[NROWS][NCOLS] = {};
friend std::ostream& operator<< <>(std::ostream&, Matrix<T, NROWS, NCOLS> const&);
};
int main()
{
Matrix<float, 10, 5> mat;
std::cout << mat;
}
Please get rid of the C header <string.h>.
You need to define Matrix before it's usage:
template<class T, size_t NROWS = 1, size_t NCOLS = 1>
class Matrix;
and add a return statement to operator<<, that returns os. You also do not need duplication of operator<< declaration, you can declare it only in a class body.
Related
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;
}
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?
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.
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;
}
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?
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.
I'm trying to make << output an entire matrix which I wrote a template for. Not sure why this isn't working, the error is:
error: no match for 'operator[]' (operand types are 'matrix<int' and 'int')
candidate is:
matrix<Comparable> matrix<Comparable>::operator[](matrix<Comparable>&) [with Comparable = int]|
no known conversion for argument 1 from 'int' to 'matrix<int>&'|
which refers to this line: o << rhs[ i ][ j ]. Am I supposed to overload [ ] as well?
matrix.h:
template <typename Comparable>
class matrix
{
private:
size_t num_cols_;
size_t num_rows_;
Comparable **array_;
public:
friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
size_t NumRows();
size_t NumCols();
};
matrix.cpp:
template <typename Comparable>
ostream& operator<< (ostream& o, matrix<Comparable> & rhs){
size_t c = rhs.NumRows();
size_t d = rhs.NumCols();
for (int i = 0; i < c; i++){
for (int j = 0; j < d; j++){
o << rhs[i][j]; //error
}
}
}
template <typename Comparable>
size_t matrix<Comparable>::NumRows(){
return num_rows_;
}
template <typename Comparable>
size_t matrix<Comparable>::NumCols(){
return num_cols_;
}
And probably irrelevant but I implemented the matrix like this:
array_ = new Comparable*[num_rows_];
for (int i = 0; i < num_rows_; i++){
array_[i] = new Comparable[num_cols_];
Your function signatures don't match:
ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
is not
template <typename Comparable>
ostream& operator<< (ostream& o, matrix<Comparable> & rhs);
Notice that one is not a template, and the other is. Also, the first has a const reference argument for rhs and the second has just a reference. This mismatch means the function you define isn't Matrix's friend, and so can't access the private (or protected members of Matrix)
What you want is to declare a templated function, and then inform the compiler that a single instantiation (say ostream& operator<<(ostream& out, const matrix<int>& val) ) is the single function you want to be friends with matrix<int>.
To do this, you need to declare that you want only a specific version of the function template, and to do that you need to declare the template, and to do that you need to declare the templated class.
Ultimately that whole chain of declarations might look something like this:
#include <iostream>
// forward declare the class so the compiler knows what's up at the
// templated operator<< declaration.
template <typename T>
class Foo;
// forward declare the templated operator<< so the compiler knows you're
// 'friend'ing a specific instantiation of this template in the
// class definition.
template <typename T>
std::ostream& operator<<(std::ostream& out, const Foo<T>& value);
template <typename T>
class Foo{
// declare the instantiation of the operator<< template that shares
// T with the class template to be a friend:
// operator<<<> can be separated out into operator<< <>
friend std::ostream& operator<<<>(std::ostream& out, const Foo<T>& value);
int bar_;
public:
Foo(int bar) : bar_{bar}{}
};
// now that you know the contents of `Foo<T>`, define the templated operator<<
template <typename T>
std::ostream& operator<<(std::ostream& out, const Foo<T>& value){
return std::cout << "Foo: " << value.bar_;
}
int main(){
Foo<int>
a{2},
b{3};
std::cout << a << '\n' << b;
}
Live on Coliru
You can read further on Template Friends on the C++ Super-FAQ.
You can declare a get function to access array_ instead of making it as an public member;
I thought friend functions had access to all members. Even in this question it worked:
C++ friend function can't access private members
The answer given in that question seems identical to my code, and his compiled fine while mine just says array_ is pivate. Anyone know why?
.h:
#ifndef matrix_h
#define matrix_h
#include <iostream>
using namespace std;
template <typename Comparable>
class matrix
{
private:
size_t num_cols_;
size_t num_rows_;
Comparable **array_;
public:
friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
size_t NumRows();
size_t NumCols();
};
#endif
.cpp:
#include <iostream>
#include "matrix.h"
using namespace std;
template <typename Comparable>
ostream& operator<< (ostream& o, matrix<Comparable> & rhs){
size_t c = rhs.NumRows();
size_t d = rhs.NumCols();
for (int i = 0; i < c; i++){
for (int j = 0; j < d; j++){
o << rhs.array_[i][j]; //not allowed
}
o << endl;
}
return o;
}
template <typename Comparable>
size_t matrix<Comparable>::NumRows(){
return num_rows_;
}
template <typename Comparable>
size_t matrix<Comparable>::NumCols(){
return num_cols_;
}
int main(){
matrix<int> a;
cout << a << endl;
}
Say you use const in both places and add const to the declarations of numRows and numCols too. Then what's the problem? Well...
You think it's identical, but your code has a template. And the friend declaration
friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
is not a template, so it doesn't match the definition
template <typename Comparable>
ostream& operator<< (ostream& o, matrix<Comparable> & rhs){ // ...
which is a template. In fact gcc will give a warning:
matrix.h:16:79: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, const matrix<Comparable>&)’ declares a non-template function [-Wnon-template-friend]
friend ostream& operator<< (ostream& o, const matrix<Comparable> & rhs);
^
matrix.h:16: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)
It's tempting to just friend all specializations, like this:
template <typename T>
friend ostream& operator<< (ostream& o, const matrix<T> & rhs);
Unfortunately, this won't work for the reasons explained here: Why can templates only be implemented in the header file? You'll be able to compile matrix.cpp, but not a separate driver program, like this:
#include <iostream>
#include "matrix.h"
using namespace std;
int main() {
matrix<int> m;
cout << m << endl;
}
You get an undefined reference error. Instead, you really should just define your entire matrix class in the header and ditch the .cpp file.
It should be pointed out that this still has a problem: you can call this operator<< just fine, but you can't, say, take its address, because it can only be found by argument-dependent lookup.
auto ptr = static_cast<ostream&(*)(ostream&, const matrix<int>&)>(operator<<); // error
For it to be found by unqualified lookup, it must have a matching declaration at namespace scope. And it is actually impossible to write such a declaration (the syntax of C++ doesn't have any way to do it)! To fix this, we need to turn operator<< into a function template, defined inline:
template <typename Comparable>
class matrix {
// ...
template <typename T>
friend ostream& operator<<(ostream& o, const matrix<T>& rhs) {
// ...
}
// ...
};
// namespace-scope declaration
template <typename T>
ostream& operator<<(ostream& o, const matrix<T>& rhs);
Now the above code taking the address of the operator will work.
The compiler complains because the function you implement is different with the one you declare(in declaration rhs is decorated by const, in implementation it isn't).
After you add const in implementation, the compiler complains "Undefined reference" because the declaration and the implementation is still not the same. The implementation is a template function, the decalartion is not.
#ifndef matrix_h
#define matrix_h
#include <iostream>
using namespace std;
template <typename Comparable>
class matrix
{
private:
size_t num_cols_;
size_t num_rows_;
Comparable **array_;
public:
template<typename T>
friend ostream& operator<< (ostream& o, const matrix<T> & rhs);
size_t NumRows() const;
size_t NumCols() const;
};
template <typename Comparable>
ostream& operator<< (ostream& o, const matrix<Comparable> & rhs){
size_t c = rhs.NumRows();
size_t d = rhs.NumCols();
for (int i = 0; i < c; i++){
for (int j = 0; j < d; j++){
o << rhs.array_[i][j]; //not allowed
}
o << endl;
}
return o;
}
template <typename Comparable>
size_t matrix<Comparable>::NumRows() const{
return num_rows_;
}
template <typename Comparable>
size_t matrix<Comparable>::NumCols() const{
return num_cols_;
}
#endif
My template class looks like this:
template<unsigned WIDTH, unsigned HEIGTH, typename T = int> class matrix { ... }
So plain and simple, the template arguments determine this size of the matrix. The size is logically constant, so I implemented it to be consant. But when I try to write a function that accepts my matrix, I run into the following problem:
std::ostream& operator<<(std::ostream &os, const matrix &m){ ...}
Writen like so, the compiler rightfully objects the lack of template arguments... But
std::ostream& operator<<(std::ostream &os, const matrix<unsigned, unsigned> &m){ ...}
triggers this error: error: expected a constant of type 'unsigned int', got 'unsigned> int'
Which is also kind of true, since matrix expects constants, not types.
How to deal with this? I'm sure I'm not the frst to encounter this problem, what's the most "canonical" way to approach this problem of passing constant-parametrized templates?
Declare your operator<<(ostream&) overload for your template class matrix as a template which should be the obvious solution here
template<unsigned WIDTH, unsigned HEIGTH, typename T = int>
class matrix
{
public:
T arr[WIDTH][HEIGTH];
};
template<unsigned WIDTH, unsigned HEIGTH, typename T>
std::ostream& operator<<(std::ostream &os, const matrix<WIDTH, HEIGTH,T> &m)
{
// formatting inserter of m onto os
return os;
}
int main()
{
matrix<10, 10> m;
std::cout << m << std::endl;
}
But generally speaking, if your operator<<(ostream&) needs access to your private data (which generally would), you would end up declaring it as friend. If you do not want to repeat the remplate parameters, place the operator<<(ostream&) non-member friend in the scope of your matrix class
template<unsigned WIDTH, unsigned HEIGTH, typename T = int>
class matrix
{
T arr[WIDTH][HEIGTH];
friend std::ostream& operator<<(std::ostream &os, const matrix &m)
{
// formatting inserter of m onto os
return os;
}
};
Option #1
Declare the operator<< as a friend function in the scope of the matrix class:
template<unsigned WIDTH, unsigned HEIGTH, typename T = int>
class matrix
{
friend std::ostream& operator<<(std::ostream &os, const matrix& m)
// ^^^^^^ plain name
{
return os;
}
};
Option #2
Make operator<< a function template as well:
template<unsigned WIDTH, unsigned HEIGHT, typename T>
std::ostream& operator<<(std::ostream &os, const matrix<WIDTH, HEIGHT, T>& m)
// ^^^^^ ^^^^^^ ^
{
return os;
}
Overloaded operator<< needs to be template as well:
template<unsigned WIDTH, unsigned HEIGHT, typename T>
std::ostream& operator<<(std::ostream &os, const matrix<WIDTH, HEIGHT, T> &m){
// ...
return os;
}