web-dev-qa-db-fra.com

Opérateur de classe de modèle surchargeant le type de retour

J'essaie de construire une classe num basée sur des modèles. Cette classe doit avoir un attribut public, val, de type T, qui est le seul paramètre basé sur un modèle. De plus, si l'on fournit une valeur, l'attribut (val) doit être initialisé avec cette valeur. Pour ce faire, j'ai créé le code suivant:

#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;
    }
};

De plus, j'ai créé la fonction main() pour tester le programme, qui ressemble à ceci:

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

Cependant, le résultat du programme est maintenant 3. Alors que je m'attendais à ce que ce soit 3.0 (De type double).

8
user12507648

Pour cela, vous devrez modifier le type de retour.

Dans votre code:

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

En effet, à l'intérieur d'un modèle de classe, vous pouvez taper le nom de la classe sans arguments de modèle et ça va être quelque peu équivalent à écrire Num<T>.

Votre fonction renvoie toujours le type du premier opérant, quel que soit le type de l'addition lui-même.

Ce que vous voulez, c'est déduire ce type provenant de l'addition:

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

De cette façon, c'est toujours le bon type de retour selon les règles de l'opérateur C++.

10
Guillaume Racicot

operator+ Doit être symétrique par rapport à ses arguments. Il vaut mieux être implémenté en tant que fonction libre plutôt qu'en tant que fonction membre pour rendre cette symétrie explicite.

Par exemple (en utilisant la déduction de type de retour 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 là pour généricité , si T et/ou U ne sont pas constructibles par défaut. Si les types sont limités aux types intégrés, comme int et double, il peut être remplacé par T{} Ou T():

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

Avec déduction d'argument de modèle de classe en C++ 17, cela peut être simplifié davantage:

template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
    return Num{x.val + y.val};
}
9
Evg