Foro de elhacker.net

Foros Generales => Dudas Generales => Mensaje iniciado por: adhar2 en 22 Marzo 2023, 09:04 am



Título: Función de amigo en C ++
Publicado por: adhar2 en 22 Marzo 2023, 09:04 am
Tengo una plantilla de función foo que debe realizar varios cálculos en función de si el parámetro de la plantilla es real o complejo. Incluso si el argumento de la plantilla es std::complexdouble>, el resultado siempre será un número real, como double. Como resultado, mi función es la siguiente:

Código:
template <class S>
struct RealType
{
  typedef S type;
};

template <class S>
struct RealType<std::complex<S> >
{
  typedef S type;
};

template <class S>
class C;

template <class S>
typename RealType<S>::type foo(C<S> &c);

template <class S>
typename RealType<std::complex<S> >::type foo(C<std::complex<S> > &c);

Ahora foo debe ser una función amiga de la clase C, así que hice la siguiente declaración:

Código:
template <class S>
class C
{
  friend typename RealType<S>::type foo(C<S> &c);
  // ...
};

Sin embargo, cuando creo Cstd::complexdouble> >, el compilador se queja de que foo no puede acceder a los miembros secretos de c. Eso funciona perfectamente con Cdouble> si sigues esta documentación. ¿Hay alguna solución (que funcione con C++ 98) para esto? Me doy cuenta de que foo no puede ser miembro de C ya que no permitiría la especialización parcial.
¿Es esto, por cierto, una especialización? Las firmas de ambas versiones de foo parecen idénticas, pero cambian un poco cuando se ingresan los tipos reales.


Título: Re: Función de amigo en C ++
Publicado por: LlopoRobot en 1 Abril 2023, 08:46 am
Sí, esto es una especialización de plantilla. En este caso, la especialización se produce para el caso en que el tipo de plantilla es std::complex<S>.

El problema que estás enfrentando es que la amistad no se hereda, lo que significa que si tienes una clase derivada de C y intentas llamar a la función foo desde la clase derivada, el compilador no permitirá el acceso a los miembros privados de la clase base. Sin embargo, hay una solución para este problema.

En lugar de hacer que foo sea un amigo de C, puedes hacer que C sea un amigo de foo. Esto permitirá que la función foo acceda a los miembros privados de C sin importar si C es una clase base o una clase derivada. Aquí está cómo se vería el código:

Código:
template <class S>
class C;

template <class S>
typename RealType<S>::type foo(C<S> &c);

template <class S>
typename RealType<std::complex<S> >::type foo(C<std::complex<S> > &c);

template <class S>
class C
{
friend typename RealType<S>::type foo<>(C<S> &c);
// ...
};

Tenga en cuenta que el <> después de foo indica que la amistad se aplica a todas las especializaciones de la función foo. También tenga en cuenta que esto debería funcionar en C++ 98 y versiones posteriores.

Espero que esto ayude a resolver su problema.


Título: Re: Función de amigo en C ++
Publicado por: RayR en 4 Abril 2023, 18:04 pm
Nota: esto debería ir en el subforo de C++. Aquí difícilmente lo van a ver los foreros que programan en C++. Yo lo vi porque me salió la respuesta de arriba en "mensajes recientes".

No sé si aún lo necesites, pero si lo mueven al subforo correcto igual a alguien le sirve después. Antes que nada, el mensaje de arriba, que parece ser copy/paste de ChatGPT-3, es un completo disparate, así que mejor ignorarlo.

Primero, ¿estás seguro de que copiaste bien esto?

Código
  1. friend typename RealType<S>::type foo(C<S> &c);

porque ahí estás declarando como amiga una función normal (no plantilla), que además va a tener precedencia sobre las que declaraste (que son plantillas), así que te debería dar error de enlazado. Si dices que te funciona bien para C<double>, supongo que más bien la declaraste así:

Código
  1. friend typename RealType<S>::type foo<S>(C<S> &c);

que es la forma correcta. Y ahí sí tienes el problema que comentas, porque con estas declaraciones:

Código
  1. template <class S>
  2. typename RealType<S>::type foo(C<S> &c);
  3.  
  4. template <class S>
  5. typename RealType<std::complex<S> >::type foo(C<std::complex<S> > &c);

nota que el parámetro de tipo para la plantilla es S en las dos. En ningún caso es std::complex<S>. Lo que el compilador hará será sobrecargar la función foo<S>: una versión recibe un parámetro de tipo C<S>&, y la otra uno de tipo C<std::complex<S> >&. Esto responde a tu otra pregunta: no, no es especialización sino sobrecarga.

Por lo tanto, si tienes algo como esto:

Código
  1. C<double> c_real;
  2. C<std::complex<double> > c_complejo;
  3. foo(c_real);
  4. foo(c_complejo)

las funciones que se generan son (omito los tipos de retorno por legibilidad):

Código
  1. foo<double>(C<double>& c);
  2. foo<double>(C<std::complex<double> >& c);

nota que las dos "se llaman" foo<double>. Sin embargo, en la clase C:

Código
  1. template <class S>
  2. class C
  3. {
  4.  friend typename RealType<S>::type foo<S>(C<S> &c);
  5.  // ...
  6. };

si declaras una variable C<std::complex<double> >, entonces, en lo que respecta a C, S es igual a std::complex<double>. Por lo tanto, lo que se estaría declarando como amiga es una hipotética función:

Código
  1. foo<std::complex<double> >(C<std::complex<double> >& c);

pero como te dije, la función que declaraste se genera como:

Código
  1. foo<double>(C<std::complex<double> >& c);

que no es amiga de C, y de ahí el error. No sé exactamente qué quieras lograr, pero posiblemente esto sea lo que buscas:

Código
  1. template <class S>
  2. class C
  3. {
  4. friend typename RealType<S>::type foo<typename RealType<S>::type>(C<S>& c);
  5. };

Así, en ambos casos se declararía como amiga foo<double> (siquiendo con los ejemplos), en su versión sobrecargada de acuerdo al tipo de C.