Sobre carga de operador [Visual Studio 2010]

Iniciado por Namida, 15 Diciembre 2010, 21:33 PM

0 Miembros y 3 Visitantes están viendo este tema.

Namida

¡Hola a todos! Tengo el siguiente problema. No se como sobrecargar un operador dentro de una plantilla. Lo he intentando de la siguiente forma y me tira error.

template<TYPE>class A{
[...] /*Resto del código*/
friend std::ostream &operator << (std::ostream &os,A<TYPE> X);
};

template<TYPE> std::ostream &operator <<(std::ostream &os,A<TYPE> X){
[...] /*Resto del código*/
return os;
};


Gracias de antemano por su ayuda ;)

Littlehorse

Y cual es el error? igual viendo el código lo mas probable es que no se encuentre la definición de la sobrecarga pero preferiría que lo confirmes así partimos del error.

Podes probar dos cosas, una es esta:

Código (cpp) [Seleccionar]
template<TYPE>class A{
friend std::ostream &operator << (std::ostream &os,A<TYPE> X){}
};


para "obligar" al compilador a ubicar la implementación.

La segunda, tal vez no la mejor pero si la mas sencilla, declarar y definir la sobrecarga en el mismo bloque:

Código (cpp) [Seleccionar]
template<TYPE>class A{
friend std::ostream &operator << (std::ostream &os,A<TYPE> X)
{
//etc
return os;
}
};


Cualquier cosa ya sabes.

Saludos.
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Namida

Muchas gracias por tu respuestas. Sí, el error es de definición. Implementando directamente la sobrecarga del operador dentro de la clase si me funciona (el segundo ejemplo que mencionas). He intentado separar la declaración de la sobrecarga de su implementación sin éxito.

De todas formas el error dado por el compilador es el siguiente:
Citar
1>------ Operación Generar iniciada: proyecto: Librerias, configuración: Debug Win32 ------
1>  main.cpp
1>main.obj : error LNK2019: símbolo externo "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class matrix<int>)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@V?$matrix@H@@@Z) sin resolver al que se hace referencia en la función _main
1>C:\Documents and Settings\mis documentos\visual studio 2010\Projects\ProyectoDePruebas\Debug\Librerias.exe : fatal error LNK1120: 1 externos sin resolver
========== Generar: 0 correctos, 1 incorrectos, 0 actualizados, 0 omitidos ==========

Sin más, gracias por la ayuda ;)

Littlehorse

Postea como has hecho el intento de separar la implementación y lo vemos. Si puedes postea una minima expresion compilable (no importa si tiene ese error) asi lo vemos mas en detalle. El primer ejemplo también tendría que funcionar, y si no funciona eso, con una forward declaration también debería funcionar.

Postea cuando puedas así lo vemos en detalle.

Saludos
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Namida


//Definición de la clase
template<typename TYPE>class matriz{
protected:
unsigned filas,columnas;
TYPE** datos;
public:
matriz (unsigned FILAS,unsigned COLUMNAS);
friend std::ostream &operator << (std::ostream &os,matriz<TYPE> X);
};

//Definición del constructor
template<typename TYPE> matriz<TYPE>::matriz(unsigned FILAS, unsigned COLUMNAS){
filas=FILAS;
columnas=COLUMNAS;
datos=new TYPE *[filas];
for(unsigned i=0;i<filas;i++){
datos[i]=new TYPE [columnas];
for(unsigned j=0;j<columnas;j++){
datos[i][j]=0;
};
};
};

//Sobrecarga del operador
template<typename TYPE> std::ostream &operator << (std::ostream &os, matriz<TYPE> X){
for(unsigned i=0;i<X.filas;i++){
for(unsigned j=0;j<X.columnas;j++){
os<<X.datos[i][j]<<'\t';
};
os<<'\n';
};
return os;
};


int main(int argc,char* argv[]){
matriz<int> A=matriz<int>(4,4);
std::cout<<A<<std::endl;
std::system("pause");
return 0;
};


Ahí te dejo el código básico de lo que, en principio, estoy intentando ejecutar. A ver si le puedes encontrar el fallo por el que no va. Sin más, gracias por todo :D

Littlehorse

Bueno usualmente no me gusta extenderme demasiado en las explicaciones al menos que sea realmente necesario, y en este caso lo es. Viendo el código ya el problema ha quedado claro, espero que la explicación también.  ;D


Cuando el compilador recorre la clase y la analiza, al encontrarse con la declaración friend de la sobrecarga del operador, en este punto:


Código (cpp) [Seleccionar]
template<typename TYPE>class matriz{
protected:
unsigned filas,columnas;
TYPE** datos;
public:
matriz (unsigned FILAS,unsigned COLUMNAS);
friend std::ostream &operator << (std::ostream &os,matriz<TYPE> X); // sobrecarga friend
};


el compilador no sabe que dichas funciones en si mismas son templates, entonces asume que la definición de dichas funciones son precisamente, no-template.
Al utilizar los métodos propiamente dichos, el compilador genera una llamada a las versiones no-template de dichos métodos, lo cual seria así:

Código (cpp) [Seleccionar]

std::ostream &operator << (std::ostream &os,matriz<TYPE> X);// Sobrecarga friend no-template


pero esos métodos nunca están definidos puesto que en realidad técnicamente no existen. Entonces en ese momento el linker te da el error de referencia indefinida.

La forma de solucionarlo es basicamente aclarándole al compilador que la versión de la función que tiene que buscar es precisamente, un template.

Primero que nada, usamos forward declarations de la clase y de la sobrecarga:

Código (cpp) [Seleccionar]
template<typename TYPE> class matriz;
template<typename TYPE> std::ostream &operator <<(std::ostream &os,matriz<TYPE> X);


y luego de eso, agregamos <> a la sobrecarga del operador para que el compilador al recorrer la clase sepa que nos estamos refiriendo a un template:

Código (cpp) [Seleccionar]
friend std::ostream &operator << <> (std::ostream &os,matriz<TYPE> X);

y luego lo defines:

Código (cpp) [Seleccionar]
template<typename TYPE> std::ostream &operator << (std::ostream &os, matriz<TYPE> X)
{
//la logica aqui
}


El código final te debería quedar así:

Código (cpp) [Seleccionar]
template<typename TYPE> class matriz; //Forward declaration de la clase
template<typename TYPE> std::ostream &operator <<(std::ostream &os,matriz<TYPE> X); // Forward declaration de la sobrecarga

//Definición de la clase
template<typename TYPE>class matriz{
protected:
unsigned filas,columnas;
TYPE** datos;
public:
matriz (unsigned FILAS,unsigned COLUMNAS);
friend std::ostream &operator << <> (std::ostream &os,matriz<TYPE> X); //Especificar versión template
};

//Definición del constructor
template<typename TYPE> matriz<TYPE>::matriz(unsigned FILAS, unsigned COLUMNAS){
filas=FILAS;
columnas=COLUMNAS;
datos=new TYPE *[filas];
for(unsigned i=0;i<filas;i++){
datos[i]=new TYPE [columnas];
for(unsigned j=0;j<columnas;j++){
datos[i][j]=0;
};
};
};

//Sobrecarga del operador
template<typename TYPE> std::ostream &operator << (std::ostream &os, matriz<TYPE> X){
for(unsigned i=0;i<X.filas;i++){
for(unsigned j=0;j<X.columnas;j++){
os<<X.datos[i][j]<<'\t';
};
os<<'\n';
};
return os;
};


int main(int argc,char* argv[]){
matriz<int> A=matriz<int>(4,4);
std::cout<<A<<std::endl;
std::system("pause");
return 0;
};



Eso o declarar y definir la sobrecarga dentro de la clase.
No lo he probado porque tengo el compilador bastante ocupado, pero si no llega a funcionar correctamente, ya sabes.

Saludos!
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Namida

Muchas gracias Littlehorse!! El problema me ha quedado bastante claro con tu explicación. Ya probé a realizarlo de dicha forma  y va sin problemas, asi que.... ¡Problema resuelto!  ;-)