[C con Clase] Templates y friend functions

Steven Davidson srd4121 en njit.edu
Mie Mar 25 19:02:00 CET 2009


Hola Vicente,

vicente lozano wrote:
> Hola,
> 
> Estaba practicando con los templates y queria hacer un template 
> "variable", que pudiera representar a todos los tipos de variables y
> sobrecargar las operaciones con el mismo significado que tienen,
> como ejercicio academico.
> 
> template <class T>
> class Var
> {
>  T _val;
>  public:
>  Var(T v=0): _val(v) {}
>  T getVar() const;
>  ...
>  template <class T>
>  friend Var operator+(const Var v, const double);
>  .....
> }
> 
> inline T Var<T>::getVar() const { return _val;}
> ....
> template <class T>
> Var operator+(const Var v, const double d)
> {
>   Var<T> res( (T) ( (double)v.getVar() + d) );
> return res;
> }
> ....
> 
> Si pongo la linea subrayada me dice declaration of class T shadows 
> template param class T.
> Si no la pongo me dice Friend declaration Var<T> operator+(const 
> Var<T>&,double) declares a non-template function
> 
> Seguro que tiene mas errores porque yo con lo de los templates aun
> estoy muy verde, conceptualmente los entiendo pero la sintaxis me
> pierde.
> 

Veamos. Escribes:

template <class T>
friend Var operator+( const Var v, const double );

En primer lugar, como dije en un correo-e anterior, deberías pasar 
objetos constantes por referencia: 'const Var &'.

Aquí, este operador es una plantilla, pero en este caso, la sobrecarga 
depende de 'Var<T>'. Como ya estamos dentro de una clase-plantilla, no 
hace falta redefinir T, que es el error que te indica el compilador, ni 
tampoco ningún otro parámetro genérico. Al final, obtendremos,

template< typename T >
class Var
{
   ...
   friend Var<T> operator+<T>( const Var<T> &v, const double );
};


También tienes un error en la implementación de la sobrecarga del 
operador +. Escribes:

template <class T>
Var operator+( const Var v, const double d )
{
   Var<T> res( (T) ( (double)v.getVar() + d) );
   return res;
}

Debes indicar que la clase 'Var' no es una clase sino una plantilla. 
Deberías escribir:

template< typename T >
Var<T> operator+( const Var<T> &v, const double d )
{
   return Var<T>( (T) ( (double)v.getVar() + d) );
}

Sinceramente, estás forzando 'v' a ser 'double'. No veo que esto sea 
apropiado según la intención que tienes. Sugiero hacer lo siguiente:

template< typename T >
Var<T> operator+( const Var<T> &var, const T &val )
{
   return Var<T>( var.getVar() + val );
}

De todas maneras, creo que es mejor definir cada especialización según 
el tipo fundamental que quieras pasar como parámetro a la plantilla. Por 
ejemplo,

template<>
class Var<double>
{
private:
   double _val;

public:
   Var( double v=0 ) : _val(v)  {}
   double getVar() const;
   ...
   friend Var<double> operator+<double>( const Var<double> &v, const 
double d );
};

template<>
Var<double> operator+<double>( const Var<double> &var, const double &val )
{
   return Var<double>( var.getVar() + val );
}

De esta forma, especializamos la definición de 'Var<double>' en lugar de 
usar la definición genérica de 'Var<>'. En el caso de los tipos 
fundamentales, aconsejaría que usaras 'long double' ya que es el "mayor" 
tipo que existe de entre los tipos fundamentales. Cualquier tipo 
fundamental puede ser promocionado a 'long double'.


Espero haber aclarado las dudas.

Steven





Más información sobre la lista de distribución Cconclase