0

I am creating a matrix class and have the following declaration. The intent is to build a scalable matrix class that has algorithms that are flexible and can be run on various kinds of platforms -

template<typename T> class xss_matrix{

 public:
 xss_matrix(int i=0, int j=0): 
  max_row(i), max_col(j)
  {

    /*create space for the (0,0) entry*/
    matrix[0]= (T*)(malloc(sizeof(T)));


  };
  ~xss_matrix()
    {
    };
  void add_entry(int row, int col, T val);
  T get_entry(int row, int col);


  friend ostream& operator<<(ostream &out, const xss_matrix<T> &m_xss_matrix);

 private:
  /*Internal variables*/
  int max_row, max_col;

  /*Internal data structures*/
  T* matrix[];


  /*Internal methods*/
  void add_columns(int row, int col);
  void add_rows(int row, int col);



};

#endif

then I overload the stream operator -

/*Overloaded stream operators*/
template<typename T> std::ostream& operator<<(ostream &out, const xss_matrix<T> &m_xss_matrix)
 {

   for(int ii = 0; ii < m_xss_matrix.max_row+1; ii+=1){
     for(int jj = 0; jj < m_xss_matrix.max_col+1; jj+=1){
       std::cout <m_xss_matrix.matrix[ii][jj] << " ";
     }
     std::cout << std::endl;
   }

 }

but when I run this -

#include "xss_matrix.hpp"

int main(int argc, char** argv)
{

  xss_matrix<double>* foo = new xss_matrix<double>;
  xss_matrix<double> bar;

  foo->add_entry(0,0,2.35);
  foo->add_entry(0,1,1.75);
  foo->add_entry(1,0,1.50);
  foo->add_entry(1,1,2.00);


  std::cout << *foo;


}

I get a linker error -

[mitra@vlch-mitra xss_src]$ make
g++ -c -o main.o main.cpp -g -I. -fpermissive 
In file included from xss_matrix.hpp:1,
                 from main.cpp:1:
xss_matrix.h:36: warning: friend declaration `std::ostream& operator<<(std::ostream&, const xss_matrix<T>&)' declares a non-template function
xss_matrix.h:36: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
g++ -o main main.o -g -I. -fpermissive 
main.o: In function `main':
/home/mitra/dv/libparser/xss_src/main.cpp:15: undefined reference to `operator<<(std::basic_ostream<char, std::char_traits<char> >&, xss_matrix<double> const&)'
collect2: ld returned 1 exit status
make: *** [main] Error 1
[mitra@vlch-mitra xss_src]$ 

I do not understand the compiler warning which I am sure causes the linker failure. Can someone help? The version of gcc is 4.4.7-4 Thanks, Raj

Rajat Mitra
  • 167
  • 1
  • 11
  • 1
    Learn to love compiler warnings! In fact, it tells you all about your problem. – SergeyA Jul 20 '18 at 14:44
  • The compiler warning even tells you how to fix it: "note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)" – Caleth Jul 20 '18 at 14:49
  • As a note: you don't need to use `new` to get an instance of a class – Caleth Jul 20 '18 at 14:51
  • Thanks Caleth and Sergey!!! The intent to use new was mostly to reclaim after use. This class has a matrix that gets to become a memory hog and is only needed for the first part of a simulation. – Rajat Mitra Jul 20 '18 at 16:31

2 Answers2

2

The friend declaration declares a non-template function named operator<<; each instantiation of xss_matrix stamps out a new declaration. None of those functions are actually defined.

Then there's a single definition of a function template, overloading all those declarations. It is not declared a friend.

However, during overload resolution, non-template overloads win, other things equal. So the compiler chooses one of those. But it's never defined, so the linker complains.

If you really want to befriend a template, it has to be done this way:

template<typename T> class xss_matrix;

template<typename T>
std::ostream& operator<<(ostream &out, const xss_matrix<T>& m_xss_matrix);

template<typename T> class xss_matrix {
  // Implementation here
  template <typename U>
  friend std::ostream& operator<<(ostream &out, const xss_matrix<U>& m_xss_matrix);
};

template<typename T>
std::ostream& operator<<(ostream &out, const xss_matrix<T> &m_xss_matrix) {
  // Implementation here
}

However, it's usually easier to have a non-friend function template simply delegate to a public member function:

template<typename T> class xss_matrix {
public:
  void print(ostream& out) {
    // Write data to out
  }
};

template<typename T>
std::ostream& operator<<(ostream &out, const xss_matrix<T> &m_xss_matrix) {
  m_xss_matrix.print(out);
  return out;
}
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • Many thanks Igor, I preferred the second approach (less code ;-) where the you advised to delegate to the public "print" function. This is now working! – Rajat Mitra Jul 20 '18 at 16:20
1

Correct declaration of a friend function template is:

template<class U>
friend ostream& operator<<(ostream &out, const xss_matrix<U> &m_xss_matrix);

Memory allocation for T* matrix[]; is incorrect. Replace T* matrix[]; with std::vector<T> matrix;. That also automatically fixes all the compiler generated copy/move constructors/assignments and the destructor (you can remove yours). Indexing by row and column is matrix[row * max_col + col].

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Hi Maxim, I will take your advice and recast matrix to 2 dimensional vector. This makes much more sense ;-) Many thanks! – Rajat Mitra Jul 20 '18 at 16:21