Typumwandlung bei Template-Argumente



  • Hallo,
    da gibts etwas was ich noch nie so richtig gerrafft habe.
    [Effektiv C++, 3. Auflage, Tipp: 47]

    Es geht um folgendes.

    template<class T>
    class Rational {
    
      public:
              Rational(const T& numerator = 0,const T& denominator = 1);
    };
    
    template<class T>
    const Rational<T> operator*(const const Rational<T>& lhs,const Rational<T>& rhs)
    { ... }
    

    Nun führ dieser Auruf zu einem Fehler:

    Rational<int> sinn(1,2);
    Rational<int> res = sinn * 2; // Fehler
    

    Bei einer NichtTemplate Klasse wäre das OK, weil die 2 implizit durch den nicht expliciten C'tor in ein Rational Objekt umgewandelt werden könnte.

    Doch bei Templates funktioniert das nicht, denn Zitat: "Impliziete Typumwandlungsfunktion werden nie zur Herleitung von Template-Argumenten herangezogen".

    Wieso ist das so, wieso erzeugt der Compiler wenn er die 2 sieht nicht einen Rational<int>, er muss sie ja sogar nicht erzeugen, weil er ja durch sinn schon eins hat.

    Die Erklärung habe ich nicht ganz verstanden.
    Frage : Kann mir das einer erklären 🙂 ???

    Der Rest ist mir dann klar, wie das dann mit friend abläuft. Nur das obere nicht, womit ich die Lücke zw. Problem und Lösung nicht schließen kann.



  • Doch bei Templates funktioniert das nicht, denn Zitat: "Impliziete Typumwandlungsfunktion werden nie zur Herleitung von Template-Argumenten herangezogen".

    Das Ganze hat nicht speziell etwas mit Typumwandlungsfunktionen zu tun. Vielmehr liegt das an den allgemeinen Deduktionsregeln.

    Bei der Herleitung von Template-Parametern wird für jedes Argument-Parameter-Paar eine passende *Substitution* gesucht. Also: "mit welchem Typ muss ich den Template-Parameter ersetzen, damit er auf das Argument passt".
    Dabei wird immer ein "exact match" gesucht bzw. sind nur wenige Konvertierungen erlaubt.

    Grundsätzlich muss man zwischen Referenz- und "normalen"-Parametern unterscheiden: falls der Parameter eine Referenz ist, dann wird nur der Typ des Arguments berücksichtig. Die einzig erlaubten Konvertierungen sind zuätzliche const/volatile Qualifaktionen sowie derived-to-base-Konvertierungen.
    Bsp:

    template <class T>
    void f(const T& a, const T& b);
    
    int main() {
      f("Hallo", "Welt"); // error
    }
    

    Der Typ von a ist const char[6]. Der von b const char[5]. T kann aber nicht beides sein -> Error.

    Andernfalls findet ein "decaying" von Array und Funktionen zu Pointern statt. Außerdem werden top-level const/volatile-Qualifikationen ignoriert.
    Andere Konvertierungen, wie z.B. benutzer-definierte, sind grundsätzlich nicht erlaubt.

    template <class T>
    void f(T a, T b);
    
    int main() {
      f("Hallo", "Welt"); // OK. T = char*
    }
    

    Am Ende müssen alle Substitutionen eindeutig sein, andernfalls schlägt die Herleitung fehl.

    In deinem Beispiel würde T für den ersten Parameter als int hergeleitet. Für den zweiten gibt es keine Herleitung. Damit ist ingesamt keine Herleitung möglich.
    Würde man benutzer-definierte Konvertierungen zulassen, dann hättest du in deinem Fall eine eigenartige Situation: du hast einen Template-Parameter, der einmal mit int und einmal mit "Konvertierung von int nach Rational<int>" substituiert wird - ein schizophrenes T.



  • OK 🙂 Vielen Dank für die Erklärung ...


Anmelden zum Antworten