¿Cómo sobrecargar correctamente el operador << para un ostream?

Estoy escribiendo una pequeña biblioteca de matriz en C ++ para operaciones de matriz. Sin embargo mi comstackdor se queja, donde antes no lo hacía. Este código se dejó en un estante durante 6 meses y entre tanto actualicé mi computadora de debian etch a lenny (g ++ (Debian 4.3.2-1.1) 4.3.2) sin embargo, tengo el mismo problema en un sistema Ubuntu con el mismo g ++ .

Aquí está la parte relevante de mi clase de matriz:

namespace Math { class Matrix { public: [...] friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix); } } 

Y la “implementación”:

 using namespace Math; std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) { [...] } 

Este es el error dado por el comstackdor:

matrix.cpp: 459: error: ‘std :: ostream & Math :: Matrix :: operator << (std :: ostream &, const Math :: Matrix &)' debe tomar exactamente un argumento

Estoy un poco confundido por este error, pero una vez más, mi C ++ se ha oxidado un poco después de hacer muchos Java esos 6 meses. 🙂

Has declarado tu función como friend . No es un miembro de la clase. Debe eliminar Matrix:: de la implementación. friend significa que la función especificada (que no es miembro de la clase) puede acceder a variables de miembros privados. La forma en que implementó la función es como un método de instancia para la clase Matrix que es incorrecto.

Solo te cuento sobre otra posibilidad: me gusta usar definiciones de amigos para eso:

 namespace Math { class Matrix { public: [...] friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) { [...] } }; } 

La función se dirigirá automáticamente al espacio de nombres circundante Math (aunque su definición aparezca dentro del scope de esa clase) pero no será visible a menos que llame al operador << con un objeto Matrix que hará que la búsqueda dependiente del argumento encuentre esa definición del operador. Eso a veces puede ayudar con llamadas ambiguas, ya que es invisible para tipos de argumentos distintos de Matrix. Al escribir su definición, también puede referirse directamente a los nombres definidos en Matrix y a Matrix, sin calificar el nombre con un posible prefijo largo y proporcionar parámetros de plantilla como Math::Matrix .

Para agregar a la respuesta de Mehrdad,

 namespace Math { class Matrix { public: [...] } std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix); } 

En su implementación

 std::ostream& operator<<(std::ostream& stream, const Math::Matrix& matrix) { matrix.print(stream); //assuming you define print for matrix return stream; } 

Suponiendo que estamos hablando de sobrecargar el operator << para todas las clases derivadas de std::ostream para manejar la clase Matrix (y no sobrecargar << para la clase Matrix ), tiene más sentido declarar la función de sobrecarga fuera del espacio de nombres Math en el encabezado

Utilice una función amiga solo si la funcionalidad no puede lograrse a través de las interfaces públicas.

Matrix.h

 namespace Math { class Matrix { //... }; } std::ostream& operator<<(std::ostream&, const Math::Matrix&); 

Tenga en cuenta que la sobrecarga del operador se declara fuera del espacio de nombres.

Matrix.cpp

 using namespace Math; using namespace std; ostream& operator<< (ostream& os, const Matrix& obj) { os << obj.getXYZ() << obj.getABC() << '\n'; return os; } 

Por otro lado, si su función de sobrecarga no necesita convertirse en un amigo, es decir, necesita acceso a miembros privados y protegidos.

Math.h

 namespace Math { class Matrix { public: friend std::ostream& operator<<(std::ostream&, const Matrix&); }; } 

Debe encerrar la definición de la función con un bloque de espacio de nombres en lugar de simplemente 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; } } 

En C ++ 14 puede usar la siguiente plantilla para imprimir cualquier objeto que tenga una T :: print (std :: ostream &) const; miembro.

 template auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os) { t.print(os); return os; }