Verschiedene Typen aus generischer Methode zurückgeben



  • Ich habe den Fall, dass ich aus ein und derselben generischen Methode (auch generischem Rückgabetyp) je nach Eingabe Ausgaben verschiedener Typen zurückgebe. Alle möglichen Ausgaben sind Klassen. Beim Kompilieren bekomme ich aber jetzt Warnungen C4927 angezeigt, dass mehr als eine implizite Konvertierung angewendet wurde. Logisch: Wenn ich halt (bspw.) die generische Methode mit Typ float aufrufe, und eine andere mögliche Ausführung der Methode aber halt int zurückgibt, muss konvertiert werden. Bei einfachen Datentypen geht das noch, aber weil ich halt Klassen benutze, die mit mehr als einer Kovertierung ineinander zu verwandeln zu sind, gibt es diese Warnung.

    Wie kann ich denn das verhindern? Die Klassen umzuschreiben ist hier jedenfalls keine Option.



  • ich sehe das Problem nicht ganz (kann dran liegen dass du keinen Code geliefert hast). Grob skizziert sieht die Situation für mich ähnlich wie folgendes aus:

    template <class T>
    struct FooResult 
    {
      typedef std::vector<std::tr1::shared_ptr<T> > type; //nur ein Beispiel...
    };
    
    template <class T>
    FooResult<T>::type foo(T const& arg)
    {
      FooResult<T>::type res;
      res.push_back(std::tr1::shared_ptr<T>(new T(arg));
      return res;
    }
    


  • Sorry, verstehe deinen Code nicht. Aber dein Einwand erscheint plausibel. Daher folgendes:

    template <class TYPE>
    TYPE TheClass<TYPE>::method(int input) {
        switch (input) {
            case 0: return 4.0f; break; // float
            case 1: return 7; break; // int
            default: return "error"; break; // char*
        }
    }
    

    So sieht's im Wesentlichen aus, nur halt mit Klassen, die sich zwar ineinander casten lassen, aber halt mit mehr als einer impliziten Kovertierung, demzufolge mir dann die Warnung ausgegeben wird, wenn ich bspw. die ganze Klasse auf float angelegt habe, aber hier jetzt z.B. ein int zurückgegeben werden muss. Das Problem ist halt, dass hier der Typ der Ausgabe nicht mit dem Typ des generischen Objektes zusammenhängen muss.



  • Ah mit dem Code versteht man schon mehr. Das Problem besteht nicht in der generischen Methode (hat damit nicht viel zu tun) sondern darin, dass du je nach Eingabe völlig inkompatible Typen zurückgibst, die zusätzlich auch nicht zum erwarteten Rückgabetyp passen. Da fragt sich was du damit erreichen willst, denn wenn du mal ein int, mal einen C-Strng zurückgibst, wie soll der Aufrufer das verarbeiten können?
    Es sei denn du möchtest tatsächlich wie der Funktionskopf vermuten lässt ein Objekt vom Typ TYPE zurückgeben und das mit dem entsprechenden Wert initialisieren - dazu muss TYPE aber den jeweiligen Konstruktor implementiert haben (fragt sich allerdings wiederum, wie sinnvoll das dann ist...), sprich: wenn du TheClass<Foo>::method aufrufen willst, muss Foo 3 Ctoren haben die jeweils einen int, ein float bzw. einen const char* als Parameter akzeptieren.
    Eventuell ist auch sinnvoll, die Konvertierung in den Rückgabetyp explizit zu machen:

    ...
    
            case 0: TYPE t(4.0f); return t; break; // float
            case 1: TYPE t(7); return t; break; // int
            default: TYPE t("error"); return t; break; // char*
    

    btw: hinter einem return ist ein break unnötig, da es nie erreicht wird (Java wäre da z.B. streng und würde unerreichbaren anprangern)



  • David Schneider schrieb:

    ...

    template <class TYPE>
    TYPE TheClass<TYPE>::method(int input) {
        switch (input) {
            case 0: return 4.0f; break; // float
            case 1: return 7; break; // int
            default: return "error"; break; // char*
        }
    }
    

    ...

    Ich denke, Du suchst an der falschen Stelle nach einer Lösung.
    1.) Erstmal passen templates (=Typbestimmung zur Compilezeit) nicht zu der Implementation (die seeeehr nach Typbestimmung zur Laufzeit aussieht).
    Mein Tipp, wenn Du das wirklich willst: union.

    2.) mal eine Gegenfrage: Wie soll der Aufrufer diese Funktion eigentlich nutzen ? Wohin soll er das Ergebnis packen und wie nutzen ?

    3.) Schonmal über Laufzeitpolymorphie mit "virtual" nachgedacht ?

    Gruß,

    Simon2.



  • pumuckl schrieb:

    ich sehe das Problem nicht ganz (kann dran liegen dass du keinen Code geliefert hast). Grob skizziert sieht die Situation für mich ähnlich wie folgendes aus:

    template <class T>
    struct FooResult 
    {
      typedef std::vector<std::tr1::shared_ptr<T> > type; //nur ein Beispiel...
    };
    
    template <class T>
    FooResult<T>::type foo(T const& arg)
    {
      FooResult<T>::type res;
      res.push_back(std::tr1::shared_ptr<T>(new T(arg));
      return res;
    }
    

    Da fehlt ein "typename". Also zwischen "template <class T>" und "FooResult<T>::type".



  • Eigentlich gebe ich nicht wirklich völlig verschiedene Typen zurück. Für ein und dieselbe Instanz der Klasse wird immer ein Wert desselben Typs zurückgebgeben, der dann auch dem Typ der Klasse entspricht. Dazu habe ich ein mode-Attribut (welches in meinem Code hier weiter unten als der Parameter auftaucht), dass immer in den richtigen Zweig leitet. Es geht also auch nicht um Typbestimmung zur Laufzeit, weil ich durchaus schon beim Kompilieren angebe, von welchem Typ die Instanz sein soll.

    Die Tatsache, dass ich hier mehrere verschiedene Typen zurückgeben lasse, liegt nur dran, dass die Klasse prinzipiell alle diese Typen zurückgeben soll. Das Problem ist dann halt, dass alle Zweige der Methode, die einen Wert von einem anderen Typ als demjenigen der Instanz zurückgeben, falsch sind.

    Natürlich könnte ich jetzt für jeden dieser möglichen Typen eine eigene Klasse schreiben, aber das ist meiner Meinung nach nicht sinnvoll, weil die verschiedenen Methoden, den benötigten Wert zu beschaffen, sich wirklich nur haarfein voneinander unterscheiden.

    Mir fällt im Moment keine Lösung ein, die das hier behebn könnte, außer halt oben genannter mit den mehreren Klassen.



  • Natürlich könnte ich jetzt für jeden dieser möglichen Typen eine eigene Klasse schreiben, aber das ist meiner Meinung nach nicht sinnvoll, weil die verschiedenen Methoden, den benötigten Wert zu beschaffen, sich wirklich nur haarfein voneinander unterscheiden.

    Schau die mal Policy-based Design an. Mit dem kannst du für das gleiche Objekt gewisse Feinheiten "ändern". Du schreibst dir dann lediglich eine Grundklasse und kannst dann zur Compile Zeit angeben was er vlt. anderst machen soll. (Das übergibst du dann als Templateparameter).

    http://en.wikipedia.org/wiki/Policy-based_design


Anmelden zum Antworten