C++ Sobrecarga de un operador por medio de un template?

Iniciado por GisiNA, 7 Mayo 2017, 22:39 PM

0 Miembros y 1 Visitante están viendo este tema.

GisiNA

Hola a todos!

Estoy tratando de aprender por mi misma los elementos básicos de C++.
He buscado en la web una respuesta a la siguiente duda, pero no la he hallado.

Quizás este problema no tiene solución.

Es posible traspasar un operador a un template de modo tal que el operador no solamente sea sobrecargado sino que a la vez sea reemplazado por otro, y de ese modo no sea necesario tener que repetir el mismo código solo por cambiar la operación, o sea, algo como esto:

Código (cpp) [Seleccionar]
Num operator +(const Num &p1,const Num &p2)
{
    Num erg;
erg.a = p1.a + p2.a;
erg.b = p1.b + p2.b;
    return erg;
}

Num operator *(const Num &p1,const Num &p2)
{
    Num erg;
erg.a = p1.a * p2.a;
erg.b = p1.b * p2.b;
    return erg;
}

Num operator -(const Num &p1,const Num &p2)
{
    Num erg;
erg.a = p1.a - p2.a;
erg.b = p1.b - p2.b;
    return erg;
}

etc....

A continuación les dejo el código. Es uno sencillo del que muchos similares pueden encontrarse en libros y en la web:

Código (cpp) [Seleccionar]
#include <iostream>

using namespace std;

class Num
{
public:
    double a, b;
};

Num operator +(const Num &p1,const Num &p2)
{
    Num erg;
erg.a = p1.a + p2.a;
erg.b = p1.b + p2.b;
    return erg;
}

int main()
{
    Num num1;
num1.a=1;
num1.b=75;
    Num num2;
num2.a=150;
num2.b=175;
    Num num1und2 = num1 + num2;

    cout << "num1 " << "a: " << num1.a << ", b: " << num1.b << endl;
    cout << "num2 " << "a: " << num2.a << ", b: " << num2.b << endl;
    cout << "num1und2 " << "a: " << num1und2.a << ", b: " << num1und2.b << endl;

    return 0;
}


Ese código me gustaría poder reescribir más o menos como sigue, de modo que sea posible traspasar al templete el operador:

Código (cpp) [Seleccionar]
#include <iostream>

using namespace std;

class Num
{
public:
    double a, b;
};

template<operator op> // * Algo por este estilo
Num operator op(const Num &p1,const Num &p2)
{
    Num erg;
erg.a = p1.a op p2.a; // * Donde dice op que sea reemplazado por el operador deseado
erg.b = p1.b op p2.b;   // * Donde dice op que sea reemplazado por el operador deseado
    return erg;
}

int main()
{
    Num num1;
num1.a=1;
num1.b=75;

    Num num2;
num2.a=150;
num2.b=175;

    Num num1und2 = num1 + num2; // * Modificando acá el operador que cambie arriba la operación

    cout << "num1und2 " << "a: " << num1und2.a << ", b: " << num1und2.b << endl;

    return 0;
}


Es posible realizar una hazaña de ese tipo?

MAFUS

Tienes hecha mal la sobrecarga.
A parte de eso puedes hacer uso de algo más primitivo que las plantillas: el preprocesador.

Un ejemplo de cómo hacerlo sería así:
Código (c++) [Seleccionar]
#include <iostream>

using namespace std;

class Num
{
public:
    double a, b;

    /* Dentro del mismo objeto, o en el archivo de implementación
     * preparo una macro que contendrá el código al que solo deberé
     * pasarle el operador que quiero. Pero cuidado pues el operador
     * deberá ser compatible. Solo sirve para ahorrar código.
     */

    #define SET_OP(op)                                                 \
    Num operator op(const Num &p)                                      \
    {                                                                  \
        Num erg;                                                       \
        erg.a = a op p.a;                                              \
        erg.b = b op p.b;                                              \
        return erg;                                                    \
    }
   
    // Hora de usar la macro con cada uno de los operadores.

    SET_OP(+);
    SET_OP(-);
    SET_OP(*);
    SET_OP(/);

    /* Elimino el símbolo de la macro por si quiero usarla más
     * adelnate con otros operadores u otro tipo de dato.
     */
     
    #undef SET_OP
};

int main()
{
    Num num1;
num1.a=1;
num1.b=75;

    Num num2;
num2.a=150;
num2.b=175;

    Num num1und2 = num1 + num2; // * Modificando acá el operador que cambie arriba la operación

    cout << "num1und2 " << "a: " << num1und2.a << ", b: " << num1und2.b << endl;

    return 0;
}

GisiNA

Hola!

Muchas gracias por tu respuesta!

Está muy bueno!  ;-)

Sin embargo, tengo el interés de saber si existe la posibilidad de hacer lo mismo sobrecargando operadores usando templates. Es un interés personal, pues, no he hallado una solución en la web ni en los libros de los que dispongo.

Quisiera saber si existe un método de esa manera, o si no es posible hacerlo de esa manera.
Mi segunda consulta va en relación al comentario

"Tienes hecha mal la sobrecarga."

Sobre qué base se levanta tu comentario?

Saludos!

MAFUS

#3
Si miras los errores que lanza el compilador, para el código que tienes, dice:
cpp.cpp:10:47: error: 'Num Num::operator+(const Num&, const Num&)' must take either zero or one argument
    Num operator +(const Num &p1,const Num &p2)

Esta sobrecarga, dice el compilador, solo puede tener ningún o un solo argumento. A partir de aquí, una búsqueda rápida por internet te lleva al código correcto para estas sobrecargas.


De todas formas no creo que te deje hacer lo que quieres porqué las plantillas son para adaptar las variables y lo que buscas es adaptar el nombre de un método.

GisiNA

Bah... a mi me compila muy bien y arroja el resultado esperado.
No sabría decirte el porqué en tu caso no compila.

Ahora bien, de regreso a mi consulta..., sabes quizás si es posible realizar lo que describo?

Saludos!

MAFUS

No deben existir templates para operadores. El hecho es que una sobrecarga de operadores, a vistas de código intermedio, son funciones y no tipos de datos. Los templates existen para adaptar tipos de datos a funciones y hacerlas genéricas. Como tu idea es cambiar a qué función llamar según el operador que quieres pasar la única forma que se me ocurre es mediante el preprocesador.

GisiNA


CalgaryCorpus

Una solucion alternativa a usar macros, pero que tampoco es exactamente lo que buscas, es usar functors. Se trata de clases o structs que tienen un metodo operator() y que puede ser redefinido en otras clases. Es eso lo que hice en el codigo que incluyo aqui:


Código (cpp) [Seleccionar]
#include <functional>
#include <iostream>

using namespace std;

// tu clase Num, con metodos adicionales para hacer mas corto el resto
class Num
{
public:
    double a, b;
    Num(double x, double y) : a(x), b(y) {}
    void show() { cout << a << ":" << b << endl; }
};

// la definicion del functor
struct Operador : public binary_function<Num,Num,Num> {
    virtual Num operator()(Num&n1, Num& n2) = 0;
};

// los operadores
struct Sumador : public Operador {
  Num operator() (Num& n1, Num& n2) {return Num(n1.a+n2.a,n1.b+n2.b);}
} sumador;

struct Multiplicador : public Operador {
  Num operator() (Num& n1, Num& n2) {return Num(n1.a*n2.a,n1.b*n2.b);}
} multiplicador;

// definir otros, si se quiere ...

// una funcion que aplica los operadores
Num apply_op(Num n1, Num n2, Operador& op) {
  return op(n1, n2);
}

int main() {
    Num num1(1,2), num2(3,4);

    apply_op(num1, num2, sumador).show();
    apply_op(num1, num2, multiplicador).show();

    // en vez de hacer .show(), como arriba, se puede asignar el resultado, si se quiere
    Num resultado = apply_op( num1, num2, sumador );
    resultado.show();

    return 0;
}
Aqui mi perfil en LinkedIn, invitame un cafe aqui