Перегрузка операторов >> и << в шаблонном классе как дружественных функций
1,00
р.
р.
const size_t SIZE = 4 Вот мой класс: template class Matrix { private: T arr[SIZE][SIZE] friend ostream& operator << (ostream &, const Matrix<T> &) friend istream& operator >> (istream &, Matrix &) void initialize() // функция которая заполняет матрицу как единичную public: Matrix() Matrix(const Matrix&) const Matrix& operator=(const Matrix&) const Matrix& operator*(const Matrix&) void operator*=(const Matrix &) T* operator[](int row) } Тут реализованы все остальные работающие операции. И тут ошибка линковки в реализации этих двух операций: 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 }<br>template< typename T> istream & operator>>(istream& is, Matrix & rhs) { for (int i(0) i < SIZE ++i) { for (int j(0) j < SIZE ++j) { is >> rhs.arr[i][j] } } return is } С чем связано возникновение ошибки?
Ответ Почему код не работает Делая следующие объявления: friend ostream& operator << (ostream &, const Matrix<T> &) friend istream& operator >> (istream &, Matrix &) Вы говорите компилятору, где-то есть функции, которые принимают потоки первым аргументом и Matrix вторым. Но Matrix здесь не является шаблоном, вопреки тому, что может показаться — шаблоном является класс Matrix, а Matrix здесь является вполне конкретной реализацией, которая будет известна в момент инстанциации шаблона. Более того, никакого кода вообще не существует, пока шаблон не был инстанциирован. Исходя из этого, наши friend-декларации только тогда вступают в силу, когда происходит инстанциация шаблона. Давайте, к примеру, создадим Matrix. Это даёт нам следующий код, сгенерированный для нашего шаблона: friend ostream& operator << (ostream &, const Matrix<int> &) friend istream& operator >> (istream &, Matrix &) Т.е. как Вы можете видеть, у нас есть две нешаблонных friend-декларации. С другой стороны у нас есть реализация шаблонного оператора(приведу только один для краткости): template< typename T> std::ostream & operator<<(std::ostream & os, const Matrix<T> & rhs) { ... } Если Вам кажется, что объявление в классе и определение вне его должны быть как-то связаны, то это не так. Это две совершенно разные функции(одна шаблонная, а другая нет), а т.к., согласно ADL(argument-dependent lookup), при вызове оператора, поиск надлежащей функции будет проведён в классе, то компилятор находит нашу friend-декларацию: friend ostream& operator << (ostream &, const Matrix<int> &) Но вот реализации этой декларации нет, отсюда и ошибка компоновщика. В лоб, для типа int, это можно было бы исправить следующий образом, надо исправить нашу реализацию на следующую: std::ostream & operator<<(std::ostream & os, const Matrix<int> & rhs) { ... } Теперь наш оператор перестал быть шаблоном и поэтому он является реализацией нашей friend-декларации, для шаблона инстанциированного для int. Как починить Разумеется, нам это не подходит — не будем же мы писать реализацию под каждый тип инстанциации. Поэтому нам нужно так написать friend-декларацию, чтобы она всегда учитывала тип T. Как это сделать? Нужно завести шаблонный оператор, как Вы и делали раньше, т.е. оставляем старую реализацию: template< typename T> std::ostream& operator<<(std::ostream & os, const Matrix<T> & rhs) { ... } Но теперь, в классе Matrix, мы добавим в друзья специализацию этого шаблона: friend ostream& operator <<<T>(ostream & os, const Matrix & rhs) friend istream& operator >>(istream &, Matrix &) А так как мы объявляем другом специализацию, то мы должны вынести определение(или только объявление) общего шаблона до класса Matrix, т.е. весь Ваш код будет выглядеть так: template 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 & rhs) { ... } template class Matrix { private: T arr[SIZE][SIZE] friend ostream& operator <<<T>(ostream &, const Matrix &) friend istream& operator >>(istream &, Matrix &) ... }
Ещё один правильный вариант, с минимумом изменений, может быть такой: friend ostream& ::operator << (ostream &, const Matrix<T> &) friend istream& ::operator >> (istream &, Matrix &) Но он работает только в студии, clang&gcc не принимают этот код.