2009-01-24 21 views
171

Estoy escribiendo una pequeña biblioteca de matriz en C++ para operaciones de matriz. Sin embargo mi compilador 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 ++¿Cómo sobrecargar correctamente el operador << para un ostream?

Aquí es la parte pertinente de mi clase de matriz:

namespace Math 
{ 
    class Matrix 
    { 
    public: 

     [...] 

     friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix); 
    } 
} 

Y la "aplicación":

using namespace Math; 

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) { 

    [...] 

} 

Este es el error dado por el compilador:

matrix.cpp:459: error: 'std::ostream& Math::Matrix::operator<<(std::ostream&, const Math::Matrix&)' must take exactly one argument

Estoy un poco confundido por este error, pero de nuevo mi C++ se ha oxidado un poco después de hacer muchos Java los 6 lun. ths. :-)

Respuesta

99

Usted ha declarado su 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.

+6

Y también debe declararlo dentro del espacio de nombres de Matemáticas (no solo con el uso del espacio de nombres Matemáticas). –

+1

¿Por qué el 'operator <<' tiene que estar en el espacio de nombres de 'Math'? Parece que debería estar en el espacio de nombres global. Estoy de acuerdo en que mi compilador quiere que esté en el espacio de nombres de 'Math', pero eso no tiene sentido para mí. –

58

Para añadir a la respuesta Mehrdad,

namespace Math 
{ 
    class Matrix 
    { 
     public: 

     [...] 


    } 
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix); 
} 

En su aplicación

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

No entiendo por qué es este un voto negativo, esto aclara que puede declarar que el operador está en el espacio de nombres y ni siquiera como amigo y cómo puede declarar al operador. – kal

+2

Mehrdad Answer no tiene ningún fragmento de código, así que simplemente agregué lo que podría funcionar al moverlo fuera de la clase en el espacio de nombres. – kal

+0

Entiendo su punto, solo miré su segundo fragmento. Pero ahora veo que sacaste al operador de la clase. Gracias por la sugerencia. –

114

Sólo le dice sobre otra posibilidad: Me gusta usar las definiciones amigo para que:

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 (e aunque su definición aparece dentro del alcance 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 de 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 la propia Matriz, sin calificar el nombre con un posible prefijo largo y proporcionar parámetros de plantilla como Math::Matrix<TypeA, N>.

+0

Gracias por esta respuesta, fue muy útil para mí. – tommyk

+4

Creo que esta respuesta es mejor que la aceptada. ¡Gracias! – PolyMesh

48

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

Utilice una función amiga solo si la funcionalidad no se puede lograr 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 hace es necesario hacer un amigo es decir, necesita tener acceso a los miembros privados y protegidos.

math.h

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

necesitará englobar la definición de función con un bloque de espacio de nombres en lugar de sólo 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; 
    }     
} 
20

En C++ 14 puede utilizar la siguiente plantilla para imprimir cualquier objeto que tiene una T :: impresión (std :: ostream &) const; miembro.

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

En realidad, esto parece funcionar también en C++ 11. – jotik

+0

¡solución interesante! Una pregunta: ¿dónde debe declararse este operador, como en un ámbito global? Supongo que debería ser visible para todos los tipos que se pueden usar para templatarlo. – barney

+0

@barney Podría estar en su propio espacio de nombres junto con las clases que lo usan. – QuentinUK

Cuestiones relacionadas