const size_t SIZE = 4; 

Here is my class:

 template<typename T> class Matrix { private: T arr[SIZE][SIZE]; friend ostream& operator << (ostream &, const Matrix<T> &); friend istream& operator >> (istream &, Matrix<T> &); void initialize();// функция которая заполняет матрицу как единичную public: Matrix(); Matrix(const Matrix<T>&); const Matrix& operator=(const Matrix<T>&); const Matrix& operator*(const Matrix<T>&); void operator*=(const Matrix<T> &); T* operator[](int row); }; 

All other running operations are implemented here.

And then linking error in the implementation of these two operations:

 template< typename T> ostream & operator<<(ostream & os, const Matrix<T> & rhs) { for (int i(0); i < SIZE; ++i) { for (int j(0); j < SIZE; ++j) { os << rhs.arr[i][j] << ' '; } os << endl; } return os; } template< typename T> istream & operator>>(istream& is, Matrix<T> & rhs) { for (int i(0); i < SIZE; ++i) { for (int j(0); j < SIZE; ++j) { is >> rhs.arr[i][j]; } } return is; } 

What is the cause of the error?

  • Why are you indexing an array of an int variable? For this there is size_t. Why not make SIZE a template parameter and not set a global constant? My implementation of matrix operations on C ++ with templates - habrahabr.ru/post/249101 - gbg
  • @gbg on int and in QVector size here . - αλεχολυτ
  • @alexolut, did you give this as an int protection? If yes, then it is unconvincing. The guys who worked and are working on Qt are not authority, absolutely. - ixSci
  • @ixSci I gave this as an alternative point of view, with a rationale. I would choose an unsigned type for the size. About authority a bit strange, because guys developing Qt are (by definition) authorities in Qt. And the presence of the sign type is also due to the specifics of Qt. The fact that their ideology does not coincide with the ideology of std:: should not, IMHO, be the subject of controversy here. As they say, in a strange monastery with its charter ... - αλεχολυτ
  • @alexolut, just their “alternativeness” creates problems in combining Qt code with standard library code and boost. But they are persistent, I created a bug on this topic a few years ago, but they did not want to change it in the 5th version. I'm not going to discuss their decision, because it was done a long time ago and now we live with it. But in modern code there is absolutely no need to use int as an index. - ixSci

3 answers 3

 friend ostream& operator << (ostream &, const Matrix<T> &); friend istream& operator >> (istream &, Matrix<T> &); 

The compiler writes warnings to these declarations:

 prog.cpp:13:64: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const Matrix<T>&)' declares a non-template function [-Wnon-template-friend] friend ostream& operator << (ostream &, const Matrix<T> &); ^ prog.cpp:13:64: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 

If you fix them like this:

 template <typename T1> friend ostream& operator << (ostream &, const Matrix<T1> &); template <typename T1> friend istream& operator >> (istream &, Matrix<T1> &); 

then everything compiles: http://ideone.com/fd6jzV

  • You have a quote on top of the code, and so conceived?) - StateItPrimitive
  • @StateItPrimitive, yes I quoted the code from the question - this is normal. But two pluses for the wrong answer at that time - not so much :) - Qwertiy
  • Thank you very much :) everything works. If it's not difficult for you to explain why you started using another template data type T1? - Kirill21
  • @Kirill, in this case, you should accept the answer with a green tick. You can still vote for him if the rating is enough. And about T1 - because the compiler said "Warning ... friendly announcement ... declares a non-generic function ... if this is not what you need ... make sure the template function is declared earlier ..." Perhaps there is more The right decision, because my makes friendly functions with any type of matrix, if I understand correctly. - Qwertiy
  • one
    But the compiler was more right than I thought ... - Qwertiy

Why code doesn't work

Making the following announcements:

 friend ostream& operator << (ostream &, const Matrix<T> &); friend istream& operator >> (istream &, Matrix<T> &); 

You tell the compiler, somewhere there are functions that take threads as the first argument and Matrix<T> second. But Matrix<T> is not a template here, contrary to what may seem - the template is the Matrix class, and Matrix<T> here is a very specific implementation that will be known when the template is instantiated. Moreover, no code at all exists until the template has been instantiated.

Based on this, our friend-declarations only take effect when a template is instantiated. Let's, for example, create a Matrix<int> . This gives us the following code generated for our template:

 friend ostream& operator << (ostream &, const Matrix<int> &); friend istream& operator >> (istream &, Matrix<int> &); 

Those. As you can see, we have two non - template friend declarations. On the other hand, we have an implementation of a template operator (I’ll only give one for short):

 template< typename T> std::ostream & operator<<(std::ostream & os, const Matrix<T> & rhs) { ... } 

If it seems to you that an ad in a class and a definition outside it should be somehow related, then this is not so. These are two completely different functions (one is template and the other is not), and since, according to the ADL (argument-dependent lookup), when the operator is called, the proper function is searched in the class, the compiler finds our friend-declaration:

 friend ostream& operator << (ostream &, const Matrix<int> &); 

But there is no implementation of this declaration, hence the linker error. In the forehead, for the type int , this could be corrected in the following way; we need to fix our implementation with the following:

 std::ostream & operator<<(std::ostream & os, const Matrix<int> & rhs) { ... } 

Now our operator has ceased to be a template, and therefore it is the implementation of our friend declaration for the template instantiated for int .

How to fix

Of course, this does not suit us - let us not write the implementation for each type of instantiation. Therefore, we need to write a friend declaration so that it always takes into account the type T How to do it? It is necessary to have a template statement, as you did before, i.e. we leave the old implementation:

 template< typename T> std::ostream& operator<<(std::ostream & os, const Matrix<T> & rhs) { ... } 

But now, in the Matrix class, we will add a specialization of this template to friends:

 friend ostream& operator <<<T>(ostream & os, const Matrix & rhs); friend istream& operator >><T>(istream &, Matrix &); 

And since we declare a specialization to be a friend, we have to move the definition (or just the declaration) of the general template to the Matrix class, i.e. Your entire code will look like this:

 template<typename T> class Matrix; template< typename T> std::ostream& operator<<(std::ostream & os, const Matrix<T> & rhs) { ... } template< typename T> std::istream & operator>>(std::istream& is, Matrix<T> & rhs) { ... } template<typename T> class Matrix { private: T arr[SIZE][SIZE]; friend ostream& operator <<<T>(ostream &, const Matrix &); friend istream& operator >><T>(istream &, Matrix &); ... }; 

Another correct option, with a minimum of changes, could be:

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

But it works only in the studio, clang & gcc do not accept this code.

  • Cool! The patterns and subtleties of their semantics in the pros are a very non-trivial topic. - VladD
  • one
    @VladD, I would even say that templates in C ++ live their own lives and no one knows all their subtleties. My hair is constantly moving when I have to go beyond the basic use of patterns - ixSci
  • Yeah, the alien SFINAE alone costs a lot of gray hair. - VladD

An alternative is listed here .

We want to declare as a friend for Matrix<T> not the entire operator pattern, but only one of its specifications with type T To do this, we need to pre-declare the full template operator. And for this we need to pre-declare a class template.

In addition, we must indicate that our operator is a template. This leads to this code:

 template<typename T> class Matrix; template<typename T> ostream & operator<< (ostream &, const Matrix<T> &); template<typename T> istream& operator >> (istream &, Matrix<T> &); template<typename T> class Matrix { private: T arr[SIZE][SIZE]; // объявляем friend'ом конкретную специализацию friend ostream& operator << <> (ostream &, const Matrix &); // обратите внимание на <> friend istream& operator >> <> (istream &, Matrix &); ... 

Check: http://ideone.com/LbBd5U


Most of this decision rightfully belongs to @Qwertiy, who understood and explained what the compiler lacks and why.