Funktionen überladen vs optionaler parameter



  • Hi Leute!

    Nehmen wir an ich habe irgendeine Funktion in der ich einen zusätzlichen Parameter haben möchte. Überlädt man die Funktion dann normalerweise oder führt man einfach einen optionalen Parameter einz oder machts jeder wie er will? Geht mir nicht um optimierung oder sowas nur um die übliche Vorgehensweise.

    //Urpsrüngliche Funktion:
    double foo(double A, double B){
        return A/B;
    }
    
    //Macht man nun:
    
    double foo(double A, double B){
        return A/B;
    }
    
    double foo(double A, double B, double C){
        return (A+C)/B;
    }
    
    //Oder macht man sowas:
    
    double foo(double A, double B, double C = 0){
        return (A+C)/B;
    }
    


  • So würde ich das machen.

    double foo(double A, double B, double C){
        return (A+C)/B;
    }
    

    Parameter standardmäßig auf irgendwelche magischen Werte zu setzen halte ich für unnötig und gefährlich.
    Standardparameter, ob nun durch Überladung oder ohne, sind meistens eine schlechte Idee. Solche Parameter neigen dazu niemals vom Aufrufer gesetzt zu werden, sodass man sich den Parameter auch hätte sparen können.
    Ausnahmen sind so Dinge wie std::vector::resize(size_type, T = T()) , die erstens allgemein bekannt und zweitens einigermaßen intuitiv und sinnvoll sind.



  • TyRoXx schrieb:

    Ausnahmen sind so Dinge wie std::vector::resize(size_type, T = T()) , die erstens allgemein bekannt und zweitens einigermaßen intuitiv und sinnvoll sind.

    Ich finde das total nervig, dass da T=T() steht und jedesmal wenn ich einen Vektor erstelle, der Default-Konstruktor von T aufgerufen einmal zu oft aufgerufen wird. Stört besonders bei n=0. Wenn, dann gehört da eine Generator-Funktion hin, die dir T bei Bedarf erstellt, so dass man es gleich dahin moven kann, wo man es haben möchte. Die bessere Lösung wäre allerdings, so eine Art Fill-Range zu übergeben, aber diese Möglichkeit findet wohl nie Einzug in die Standardlib.

    Überladung ist besser als optionale Paramter und verschiedene Benennung ist (in diesem Fall) besser als Überladung. Überladung ist super in Verbindung mit ADL und generischem Code, aber wenn Funktionen was unterschiedliches machen, dann sollen die anders heissen.

    Also hier:

    double foo(double A, double B) {
        return A/B;
    }
    
    // Sprechenden Name ausdenken!
    double foo_with_sum(double A, double B, double C) {
        return (A+C)/B;
    }
    


  • ess das ell schrieb:

    TyRoXx schrieb:

    Ausnahmen sind so Dinge wie std::vector::resize(size_type, T = T()) , die erstens allgemein bekannt und zweitens einigermaßen intuitiv und sinnvoll sind.

    Ich finde das total nervig, dass da T=T() steht und jedesmal wenn ich einen Vektor erstelle, der Default-Konstruktor von T aufgerufen einmal zu oft aufgerufen wird. Stört besonders bei n=0. Wenn, dann gehört da eine Generator-Funktion hin, die dir T bei Bedarf erstellt, so dass man es gleich dahin moven kann, wo man es haben möchte. Die bessere Lösung wäre allerdings, so eine Art Fill-Range zu übergeben, aber diese Möglichkeit findet wohl nie Einzug in die Standardlib.

    Wenn du keine Objekte konstruieren willst, nimm reserve und push_back...



  • Nathan schrieb:

    ess das ell schrieb:

    TyRoXx schrieb:

    Ausnahmen sind so Dinge wie std::vector::resize(size_type, T = T()) , die erstens allgemein bekannt und zweitens einigermaßen intuitiv und sinnvoll sind.

    Ich finde das total nervig, dass da T=T() steht und jedesmal wenn ich einen Vektor erstelle, der Default-Konstruktor von T aufgerufen einmal zu oft aufgerufen wird. Stört besonders bei n=0. Wenn, dann gehört da eine Generator-Funktion hin, die dir T bei Bedarf erstellt, so dass man es gleich dahin moven kann, wo man es haben möchte. Die bessere Lösung wäre allerdings, so eine Art Fill-Range zu übergeben, aber diese Möglichkeit findet wohl nie Einzug in die Standardlib.

    Wenn du keine Objekte konstruieren willst, nimm reserve und push_back...

    Ich habe nicht gesagt, dass es keine Workarounds gibt, sondern, dass die Memberfunktion mit dem Default-Argument schlecht designed ist, und zwar genau wegen dem Standardargument. vector::resize(size_type) als separate Memberfunktion zu haben wäre effizienter.


  • Mod

    ess das ell schrieb:

    TyRoXx schrieb:

    Ausnahmen sind so Dinge wie std::vector::resize(size_type, T = T()) , die erstens allgemein bekannt und zweitens einigermaßen intuitiv und sinnvoll sind.

    Ich finde das total nervig, dass da T=T() steht und jedesmal wenn ich einen Vektor erstelle, der Default-Konstruktor von T aufgerufen einmal zu oft aufgerufen wird. Stört besonders bei n=0.

    Phantomschmerzen?



  • ess das ell schrieb:

    vector::resize(size_type) als separate Memberfunktion zu haben wäre effizienter.

    Inwiefern? Man spart sich den einen move vom Argument zum Speicher?



  • Ich habe es gerade einmal nachgeschlagen und feststellen müssen, dass vector::resize in C++11 repariert wurde.

    Es sieht nun so aus:

    void resize( size_type count );
    void resize( size_type count, const value_type& value );
    

    Wo gibt es in der Standardbibliothek überhaupt noch Default-Parameter? So Sachen wie binary_search sind auch schon immer überladen.



  • Ausserdem lässt sich ein Funktions- / Methodenzeiger bei Funktionen und Methoden mit Defaultargumenten nur erstellen, wenn der Zeiger selbst so viele Parameter hat, wie die Funktion auch (wobei auch die Parameter mit Defaultargument im Zeigertyp und beim Aufruf vorkommen müssen). Sehr unkomfortabel (jaja std::function aber das gibt's erst mit C++11 (ich weiss, dass man es in C++03 implementieren kann) und fordert ausserdem dynamischen Speicher an).



  • asfdlol schrieb:

    (jaja std::function aber das gibt's erst mit C++11 (ich weiss, dass man es in C++03 implementieren kann) und fordert ausserdem dynamischen Speicher an).

    std::bind braucht keinen dynamischen Speicher, wenn es richtig gemacht ist.



  • TyRoXx schrieb:

    Parameter standardmäßig auf irgendwelche magischen Werte zu setzen halte ich für unnötig und gefährlich.

    Warum?

    Standardparameter, ob nun durch Überladung oder ohne, sind meistens eine schlechte Idee.

    Warum?

    Solche Parameter neigen dazu niemals vom Aufrufer gesetzt zu werden, sodass man sich den Parameter auch hätte sparen können.

    Nein, denn die Version mit 3 Parametern könnte aus der Programmierersicht immer noch sinnvoll sein. Die alternative wäre eine weitere Funktion mit anderem Namen mit eben diesen parametern hinzuzufügen, aber das sugeriert dem Anwender eine Wichtigkeit der Funktion, die diese nicht hat.

    (Ich hab ein recht gutes Beispiel dafür. Ich muss ein paar teure numerische Verfahren berechnen, die intern auch ein paar recht große matrizen und vektoren miteinander multiplizieren. In einigen Anwendungen werden eben jene Multiplikationen aber schon durchgeführt -> weiterer optionaler parameter der die Laufzeit erheblich reduziert. Dazu gibt es dann die überladene Variante, die diese multiplikationen vorberechnet und dann die andere Funktion aufruft.)

    Ausnahmen sind so Dinge wie std::vector::resize(size_type, T = T()) , die erstens allgemein bekannt und zweitens einigermaßen intuitiv und sinnvoll sind.

    Warum ist Bekanntheit ein positives Kriterium? Ist das ein Argument in dem Sinne von "ist halt schon lange da, also darf es so bleiben"? Für den Aufrufer macht es ja keinen Unterschied. Und warum glaubst du, dass die meisten Standardparameter nicht intuitiv sind?


Log in to reply