Как правильно перегрузить оператор << для потока?
Я пишу небольшую матричную библиотеку в С++ для операций с матрицами. Однако мой компилятор жалуется, где раньше этого не было. Этот код остался на полке в течение 6 месяцев, а между мной я обновил свой компьютер от debian etch до lenny (g++ (Debian 4.3.2-1.1) 4.3.2
) однако у меня такая же проблема в системе Ubuntu с тем же g++.
Вот соответствующая часть моего матричного класса:
namespace Math
{
class Matrix
{
public:
[...]
friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
}
}
И "реализация":
using namespace Math;
std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {
[...]
}
Это ошибка, заданная компилятором:
matrix.cpp: 459: ошибка: 'std:: ostream & Math:: Матрица:: Оператор < < (станд:: ostream &, const Math:: Matrix &) 'должен принимать ровно один аргумент
Я немного смущен этой ошибкой, но опять же мой С++ стал немного ржавым после того, как много Java за эти 6 месяцев.: -)
Ответы
Ответ 1
Вы указали свою функцию как friend
. Это не член класса. Вы должны удалить Matrix::
из реализации. friend
означает, что указанная функция (которая не является членом класса) может обращаться к частным переменным-членам. Способ, которым вы реализовали функцию, похож на метод экземпляра для класса Matrix
, который является неправильным.
Ответ 2
Просто расскажу вам о другой возможности: мне нравится использовать определения друзей для этого:
namespace Math
{
class Matrix
{
public:
[...]
friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
[...]
}
};
}
Функция будет автоматически нацелена на окружающее пространство имен Math
(даже если его определение появляется в пределах области действия этого класса), но не будет видимым, если вы не вызываете оператор < < с объектом Matrix, который заставит зависящий от аргумента поиск найти это определение оператора. Иногда это может помочь с неоднозначными вызовами, поскольку оно невидимо для типов аргументов, отличных от Matrix. При написании своего определения вы также можете ссылаться непосредственно на имена, определенные в матрице и на матрицу, без определения имени с некоторым возможно длинным префиксом и предоставления параметров шаблона, таких как Math::Matrix<TypeA, N>
.
Ответ 3
Чтобы добавить к Mehrdad ответ,
namespace Math
{
class Matrix
{
public:
[...]
}
std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}
В вашей реализации
std::ostream& operator<<(std::ostream& stream,
const Math::Matrix& matrix) {
matrix.print(stream); //assuming you define print for matrix
return stream;
}
Ответ 4
Предполагая, что мы говорим о перегрузке operator <<
для всех классов, полученных из std::ostream
для обработки класса Matrix
(а не для перегрузки <<
для класса Matrix
), имеет смысл объявить функция перегрузки вне пространства имен Math в заголовке.
Использовать функцию друга только в том случае, если функциональность не может быть достигнута через общедоступные интерфейсы.
Matrix.h
namespace Math {
class Matrix {
//...
};
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);
Обратите внимание, что перегрузка оператора объявляется вне пространства имен.
Matrix.cpp
using namespace Math;
using namespace std;
ostream& operator<< (ostream& os, const Matrix& obj) {
os << obj.getXYZ() << obj.getABC() << '\n';
return os;
}
С другой стороны, если ваша функция перегрузки должна быть сделана, то другу нужен доступ к закрытым и защищенным членам.
Math.h
namespace Math {
class Matrix {
public:
friend std::ostream& operator<<(std::ostream&, const Matrix&);
};
}
Вам нужно приложить определение функции блоком пространства имен вместо using namespace Math;
.
Matrix.cpp
using namespace Math;
using namespace std;
namespace Math {
ostream& operator<<(ostream& os, const Matrix& obj) {
os << obj.XYZ << obj.ABC << '\n';
return os;
}
}
Ответ 5
В С++ 14 вы можете использовать следующий шаблон для печати любого объекта, который имеет T:: print (std:: ostream &) const; член.
template<class T>
auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os)
{
t.print(os);
return os;
}