Legitim, Eingabeparameter zu verändern?



  • Abend!

    Sagen wir ich habe so eine Funktion:

    void Foo(const double *a, const double *b, double *c, double *d, double *x, unsigned int n);
    

    a,b,c,d sind reine Eingabeparameter und das Ergebnis des Algorithmus' wird in x geschrieben. Aus Effizienzgründen könnte ich jetzt in der Funktion Foo den Speicher von c und d wiederverwenden und Zwischenergebnisse in c und d speichern.
    Der Vorteil ist klar: Effizienz. In diesem konkreten Fall spar ich mir 2 new und 2 delete, aber es geht hier auch eher ums Prinzip.
    Nachteil: Es könnte den Nutzer verwirren, wieso von den Eingabeparametern nur a,b const sind unc nicht auch noch c und d.

    Wie würdet ihr das regeln? Also ich tendiere stark dazu den Speicher wieder zu verwenden (sprich c und d non-const lassen) und dann gut dokumentieren (in der Doku klar machen, dass c und d inout Parameter sind).



  • Darf man fragen weshalb die Eingabeparameter überhaupt Zeiger sind? Und wenn du Werte zwischenspeichern willst, warum nimmst du nicht den Stack (also deklarierst normal float tmp = ...; ) sondern den teuren Heap?



  • Ich habs ja fast befürchet...
    Hängt euch bitte nicht an den Pointern auf. Das ist nur ein Beispiel, die Parameter können irgendwas sein, z.B: Referenzen auf Vektoren oder oder.
    Mir geht es ums PRINZIP. Ob es in Ordnung ist Referenzparameter/Pointer in der Funktion zu benutzen, obwohl es eigentlich reine Eingabeparameter ist, aber man durch die Wiederverwendung U.U. teure Allokationen spart.



  • Badestrand schrieb:

    Darf man fragen weshalb die Eingabeparameter überhaupt Zeiger sind? Und wenn du Werte zwischenspeichern willst, warum nimmst du nicht den Stack (also deklarierst normal float tmp = ...; ) sondern den teuren Heap?

    Man beachte das unsigned int n .

    alibarba schrieb:

    Abend!
    (...)
    Also ich tendiere stark dazu den Speicher wieder zu verwenden (sprich c und d non-const lassen) und dann gut dokumentieren (in der Doku klar machen, dass c und d inout Parameter sind).

    Das mag in ganz ganz ganz GANZ seltenen Fällen Sinn machen. WENN man wirklich so einen ganz seltenen Fall hat, dann würde ich auf jeden Fall die Funktion a) wenn irgend möglich nur als "Implementierungs-Detail" irgendwo rumgammeln haben und b) versuchen einen Namen für die Funktion (und die Parameter) zu finden, der bereits impliziert was hier passiert.

    Weitaus häufiger werden aber Fälle sein wo
    a) es entweder komplett egal ist was hier an Zeit für new/delete verbraucht wird, weil es für die Gesamtlaufzeit keinen nennenswerten Unterschied macht oder
    b) es elegantere Möglichkeiten gibt, hier für weniger "verschwendete" Zeit zu sorgen.



  • alibarba schrieb:

    Ich habs ja fast befürchet...

    Dann hättest du einen deutlicheren Hinweis in deinem Beitrag verankern sollen als ein zugegebenermassen etwas vages unsigned int n (welches vermuten lässt dass es sich um Zeiger auf Arrays handelt, und nicht um Zeiger auf einzelne Werte).

    Mir geht es ums PRINZIP. Ob es in Ordnung ist Referenzparameter/Pointer in der Funktion zu benutzen, obwohl es eigentlich reine Eingabeparameter ist, aber man durch die Wiederverwendung U.U. teure Allokationen spart.

    Das ist absolut nicht in Ordnung, da es dann keine Input-Parameter mehr sind, sondern in/out Parameter. Auch wenn das "out" keinen interessiert, aber da du sie änderst... sind es eben keine Input-Parameter mehr.

    Wenn der neue Standard mal durch ist, kannst du r-value references verwenden.

    Bis dahin bleibt nur die Möglichkeit sprechende Namen zu verwenden, und zu beten (*). Da ich aber nicht gerne bete, und es wie gesagt kaum Fälle gibt wo es notwendig ist solche Abscheulichkeiten zu verwenden...

    (*): Doku liest keiner. Zumindest nicht bevor irgendwas fertig implementiert ist, und dann beim Testen crasht. Ist ein Erfahrungswert 😉



  • p.S.: eine oft anwendbare Möglichkeit das "besser" zu lösen:

    int Foo(std::string const& a, std::string const& b);
    int Foo(std::string const& a, std::string const& b, std::string& scratch);
    

    Soll heissen: lass dem Programmierer die Wahl, ob er den "Scratch-Space" mitliefern will, oder nicht.

    Dazu machst du 1) verschiedene Overloads und 2) verwendest du den "Scratch-Space" *nur* als Scratch-Space, und *nicht* gleichzeitig als Input-Parameter.

    Bei der Variante ist es viel unwahrscheinlicher, dass jemand unabsichtlich "drüberstolpert".


Anmelden zum Antworten