Operador de clase con plantilla + tipo de retorno de sobrecarga

8

Estoy tratando de construir una clase num con plantilla. Esta clase debe tener un atributo público val, con tipo T, que es el único parámetro con plantilla. Además, si uno proporciona un valor, el atributo ( val) debe inicializarse con este valor. Para hacerlo hice el siguiente código:

#include <iostream>

template<class T>
class Num {
public: 
    T val;

    Num():val(0) { std::cout<<"default constr used"<<std::endl; }
    Num(T value):val(value) {std::cout<<"constr (T value) used"<<std::endl; }
    ~Num() { std::cout<<"destructor used"<<std::endl; }

    template<typename U>
    Num operator+(const Num<U>& other) {
        return val+other.value;
    }
};

Además, creé la main()función para probar el programa, que se ve así:

int main() {
    std::cout << Num<int>(1) + Num<double>(2.0);
    return 0;
}

Sin embargo, el resultado del programa es ahora 3. Mientras que esperaba que fuera 3.0(de tipo double).

Racicot Guillaume
fuente
Si desea duplicar ya que tiene el código escrito ahora, deberá voltear esos operandos. Num<int>(1) + Num<double>(2.0);es lo mismo Num<int>(1).operator+(Num<double>(2.0))que has declarado que devuelve un valor de tipo Num<int>.
scohe001
1
Esto ni siquiera se compilará como es.
Ted Lyngmo
¿Por qué eso daría un error de compilación?
Juan Carlos Ramirez
3
@JuanCarlosRamirez Porque la clase no tiene una variable miembro llamada value.
Ted Lyngmo
Tienes razón, no vi eso.
Juan Carlos Ramirez

Respuestas:

10

Para eso necesitarás cambiar el tipo de retorno.

En su código:

// vvv---- Means Num<T>
   Num operator+(const Num<U>& other) {
       return val + other.val;
   }

De hecho, dentro de una plantilla de clase, puede escribir el nombre de la clase sin argumentos de plantilla y será algo equivalente a escribir Num<T>.

Su función siempre devuelve el tipo de la primera operante, sin importar el tipo de la suma en sí.

Lo que quieres es deducir ese tipo a partir de la adición:

auto operator+(const Num<U>& other) -> Num<decltype(val + other.val)> {
    return val + other.val;
}

De esa manera, siempre es el tipo de retorno correcto de acuerdo con las reglas del operador de C ++.

Racicot Guillaume
fuente
2
en c ++ 14 y posterior, no es necesario-> Num<decltype(val + other.val)>
BЈовић
@ BЈовић, él lo necesita. De lo contrario, el tipo de retorno sería decltype(val + other.val), y no Num<decltype(val + other.val)>.
Evg
@ BЈовић En C ++ 17 usaría la deducción de tipo de retorno y la deducción de argumento de plantilla de clase para regresarNum{val + other.val}
Guillaume Racicot
9

operator+debe ser simétrico con respecto a sus argumentos. Es mejor implementarlo como una función libre en lugar de una función miembro para hacer explícita esta simetría.

Por ejemplo (usando la deducción de tipo de retorno C ++ 14):

template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
    using R = decltype(std::declval<T>() + std::declval<U>());
    return Num<R>{x.val + y.val};
}

std::declval<T>()está ahí para el carácter genérico , si Ty / o Uno son construibles por defecto. Si los tipos se limitan a los incorporados, como inty double, se puede reemplazar con T{}o T():

using R = decltype(T{} + U{});

Con la deducción de argumentos de plantilla de clase en C ++ 17 se puede simplificar aún más:

template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
    return Num{x.val + y.val};
}
Evg
fuente
Wow, no tenía idea de que podrías autoeliminar un tipo de devolución. Esto está muy bien.
scohe001
@ scohe001, es la función C ++ 14.
Evg