Vorlagenklassenoperator + Überladungsrückgabetyp

8

Ich versuche, eine Num-Klasse mit Vorlagen zu erstellen. Diese Klasse muss ein öffentliches Attribut valmit dem Typ type haben T, der der einzige Vorlagenparameter ist. Wenn man einen Wert angibt, valsollte das Attribut ( ) mit diesem Wert initialisiert werden. Dazu habe ich folgenden Code erstellt:

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

Außerdem habe ich die main()Funktion zum Testen des Programms erstellt, die folgendermaßen aussieht:

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

Das Ergebnis des Programms ist jedoch jetzt 3. Während ich damit gerechnet habe 3.0(vom Typ double).

Guillaume Racicot
quelle
Wenn Sie doppelt so viel wie jetzt geschrieben haben möchten, müssen Sie diese Operanden umdrehen. Num<int>(1) + Num<double>(2.0);ist dasselbe, Num<int>(1).operator+(Num<double>(2.0))das Sie als Rückgabe eines Wertes vom Typ deklariert haben Num<int>.
scohe001
1
Dies wird nicht einmal so kompiliert, wie es ist.
Ted Lyngmo
Warum würde das einen Kompilierungsfehler geben?
Juan Carlos Ramirez
3
@JuanCarlosRamirez Da die Klasse keine Mitgliedsvariable mit dem Namen hat value.
Ted Lyngmo
Du hast recht, habe das nicht gesehen.
Juan Carlos Ramirez

Antworten:

10

Dazu müssen Sie den Rückgabetyp ändern.

In Ihrem Code:

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

In der Tat können Sie in einer Klassenvorlage den Namen der Klasse ohne Vorlagenargumente eingeben, was dem Schreiben in gewisser Weise entspricht Num<T>.

Ihre Funktion gibt immer den Typ des ersten Operanten zurück, unabhängig vom Typ der Addition.

Was Sie wollen, ist, diesen Typ aus der Addition abzuleiten:

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

Auf diese Weise ist es immer der richtige Rückgabetyp gemäß den C ++ - Operatorregeln.

Guillaume Racicot
quelle
2
in c ++ 14 und höher brauchen Sie nicht-> Num<decltype(val + other.val)>
BЈовић
@ BЈовић, er braucht es. Andernfalls wäre der Rückgabetyp decltype(val + other.val)und nicht Num<decltype(val + other.val)>.
Evg
@ BЈовић In C ++ 17 würde ich Rückgabetyp Abzug und Klassenvorlagenargument Abzug verwenden, um zurückzugebenNum{val + other.val}
Guillaume Racicot
9

operator+sollte in Bezug auf seine Argumente symmetrisch sein. Es ist besser, als freie Funktion als als Elementfunktion implementiert zu werden, um diese Symmetrie explizit zu machen.

Zum Beispiel (unter Verwendung des C ++ 14-Rückgabetypabzugs):

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>()ist für die Allgemeinheit da , wenn Tund / oder Unicht standardmäßig konstruierbar sind. Wenn Typen auf integrierte Typen wie intund beschränkt sind double, können sie durch T{}oder ersetzt werden T():

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

Mit dem Abzug von Klassenvorlagenargumenten in C ++ 17 kann dies weiter vereinfacht werden:

template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
    return Num{x.val + y.val};
}
Evg
quelle
Wow, ich hatte keine Ahnung, dass Sie autoeinen Rückgabetyp entfernen könnten . Das ist ziemlich cool.
scohe001
@ scohe001, es ist C ++ 14-Funktion.
Evg