const string * == const string * const



  • großbuchstaben schrieb:

    beim Überfliegen einer längeren Deklarationsliste von Funktionen und Methoden ist es mir sehr wohl wichtig, sofort und auf einen Blick zu erkennen, ob ein Parameter const ist oder nicht (d.h. dieser Parameter ist/ist nicht potentielle Rückgabe).

    Nein, ist es nicht. Wie gesagt, Top-Level-CV-Qualifizierer bringen dem Aufrufer Null relevante Informationen. Es kann ihm komplett egal sein, ob die Kopie innerhalb der Funktion verändert wird oder nicht.

    Was du meinst, ist eben nicht Top-Level.

    void Function(const T& x); // ok
    void Function(const T* x); // ok
    void Function (const T x); // sinnlos
    

  • Mod

    void Function (const T x); // sinnlos
    

    Ja, das ist sinnnlos - bei einer Deklaration ist das redundant. Aber nicht bei der Definition, zumindest für die Funktion. Man kann ja auch bei der Deklaration der Funktion non- const Parameter deklarieren und bei der Definition diese als const qualifizieren.

    Du definierst also zwei Mal die gleiche Funktion.

    Danke. 👍



  • void Function (const T x); // sinnlos
    

    nein, ist es nicht. Es ist WIRKUNGSlos. Aber nicht SINNlos.

    Denn der SINN von Quelltext-Stilistik ist es, dem Leser möglichst schnell klar zu machen, was wichtig ist. Und daß ein Parameter keine Rückgabe ist, ist mir beim Überfliegen erst mal am Wichtigsten. Denn bevor ich mich mit den Details einer Funktion befasse, will ich als allererstes wissen, was ist Eingabe, was ist Ausgabe.

    Dem Compiler ist es egal. Mir (als Leser meiner eigenen Quelltexte) nicht.



  • großbuchstaben schrieb:

    nein, ist es nicht. Es ist WIRKUNGSlos. Aber nicht SINNlos.

    Doch, in der Deklaration ist es komplett sinnlos -- du verkomplizierst die Schnittstelle mit nutzlosen Informationen (mit Implementierungsdetails). Wie Arcoth nochmals hervorgehoben hat, du definierst also zwei Mal die gleiche Funktion.

    großbuchstaben schrieb:

    Und daß ein Parameter keine Rückgabe ist, ist mir beim Überfliegen erst mal am Wichtigsten.

    Erklär mir bitte, wie man

    void Function (T x);
    

    hier x als Output-Parameter benutzen kann (falls T z.B. ein Klassentyp ist).



  • Nexus schrieb:

    Erklär mir bitte, wie man

    void Function (T x);
    

    hier x als Output-Parameter benutzen kann (falls T z.B. ein Klassentyp ist).

    Finde die Output-Parameter:

    void Function(int i, void *p, double x, const std::string &data, std::vector<int> &ret);
    

    Und jetzt nochmal:

    void Function(const int i, void *p, const double x, const std::string &data, std::vector<int> &ret);
    

    Welches ist lesbarer?



  • Wer Input- und Output-Parameter in beliebiger Reihenfolge mixt, ist komplett selbst schuld. Davon abgesehen, dass das Beispiel gekünstelt und die Variablennamen mies gewählt sind.


  • Mod

    Welches ist lesbarer?

    Erstes



  • nwp3 schrieb:

    Finde die Output-Parameter:

    void Function(int i, void *p, double x, const std::string &data, std::vector<int> &ret);
    

    Und jetzt nochmal:

    void Function(const int i, void *p, const double x, const std::string &data, std::vector<int> &ret);
    

    Welches ist lesbarer?

    Das hat viel damit zu tun, was man gewohnt ist und was üblicher ist.
    Und das ist definitiv das Erste!
    Ich würde beim Zweiten lange hängen bleiben, weil ich einen Fehler vermuten würde, weil irgendwas ausgedrückt wird/werden soll, was hier keinen Sinn macht.



  • Hmm, kann ich nicht nachvollziehen. Aber wenn ihr bei void foo(const int) echt ewig überlegt was der Sinn sein soll, dann ist es halt so. Vielleicht eine Gewöhnungs- bzw. Konsistenzsache.



  • Jockelx schrieb:

    Ich würde beim Zweiten lange hängen bleiben, weil ich einen Fehler vermuten würde, weil irgendwas ausgedrückt wird/werden soll, was hier keinen Sinn macht.

    Genau das. So gestaltete APIs machen auf mich direkt einen schlechten Eindruck, weil ich nicht weiss, ob was vergessen wurde oder die Entwickler gewisse C++-Prinzipien nicht verstanden haben.

    Schaut euch nur mal diverse etablierte C++-Bibliotheken an. Top-Level- const für Parameter benutzt nämlich niemand. Da 90% der Parameter reine Input-Parameter sind, müsste man extrem viele unnötige Schlüsselwörter hinschreiben, welche 1. die ganze Signatur weniger leserlich machen und 2. die sinnvollen Fälle für const (nämlich Zeiger/Referenz darauf) untergehen lassen, wodurch auch die Sensibilität für diese sinkt. Warum sollte man dem Aufrufer explizit mitteilen, dass der Parameter nicht geändert wird, obwohl das der Normalfall ist? Viel sinnvoller ist es, die Ausnahmefälle, also Output-Parameter kennzuzeichnen, was man mit & und * tut.

    Klar ist vieles Stilfrage, aber meines Erachtens sollte man -- zumindest wenn seinen Code teilt -- sich auch an gewisse etablierte Konventionen halten, die es für andere Leute einfacher machen, den Code zu verstehen. Dazu gehören unter anderem aussagekräftige Parameternamen, massvoller Einsatz von const und keine absichtlich verwirrende Reihenfolge -- also ziemlich genau nicht so wie im Beispiel von nwp3.



  • const kann man kaum oft genug verwenden, sofern es die Anwendbarkeit nicht einschränkt.

    Ein "const" in der Parameterliste ist nunmal deutlicher als wenn ich mir den "Fliegensch|*s" aus & und * ansehen muß. Und was die Reihenfolge von E/A-Parametern angeht, habe ich schon Funktionen gesehen, die die Ausgaben zuerst machen, und solche, die die Ausgaben zuletzt machen. Was mich wiederum zwingt, den "Fliegensch|*s" zu studieren, wenn kein "const" da steht.

    ... und noch eins:

    Ich schreibe meinen Code nicht für den Compiler -- dann könnte ich ja viel Zeit sparen, indem ich die Bezeichner durchnummeriere a la class C1 { }; ... class C2 { ...} ; int C3(const (sic) vector<C4> C5); ... -- sondern ich schreibe Code für mich bzw die übrigen Leser meines Quellcodes.

    Dem Compiler Arbeit zu sparen ist für mich kein Argument, solange die Alternative ist, den Code schneller erfaßbar zu gestalten.


  • Mod

    const kann man kaum oft genug verwenden, sofern es die Anwendbarkeit nicht einschränkt.

    Äh, Quatsch.

    Ein "const" in der Parameterliste ist nunmal deutlicher

    Nein, und wenn du deine Ansicht nicht änderst, wird mit dir keiner arbeiten wollen. Denn abgesehen von dir findet das so gut wie keiner lesbarer. Du ziehst an den Haaren irgendeinen Firlefanz herbei um dein längst widerlegtes Argument noch über Wasser zu halten.



  • großbuchstaben schrieb:

    const kann man kaum oft genug verwenden, sofern es die Anwendbarkeit nicht einschränkt.

    Ich nutze const zwar intensiv, aber niemals bei "By-Value"-Parametern. Diese werden kopiert, und daher bringt mir das const hier keinerlei Vorteil. Es mag "konsistenter" nicht aber unbedingt lesbarer sein wenn man diese mit const versieht. Zudem liefert das const an dieser Stelle auch weder eine Nutzinformation, noch einen Vorteil.



  • seit wann entscheiden wir denn demokratisch, was ich lesbarer zu finden habe ? 😃

    Aber egal. Wer ein wirklich gutes Gegenargument (außer "alle machen aber X ...") hat, kann sich ja melden.



  • großbuchstaben schrieb:

    Aber egal. Wer ein wirklich gutes Gegenargument (außer "alle machen aber X ...") hat, kann sich ja melden.

    Scheint leider nichts zu bringen.



  • Das beste Gegenargument ist wohl, dass es kein gutes Argument dafür gibt.



  • Es gibt drei Anwendungsfälle für by-Value:
    1. Kleine, trivial kopierbare Typen: Nur in diesem Fall greift die Debatte. Und das Argument, dass man das besser von Outputparametern unterscheiden kann, greift eh nur, wenn man Outputparameter hat (was bei mir fast nie auftritt). Das heißt wir diskutieren hier über einen minimalen Anwendungsfall. Und bei diesem minimalen Anwendungsfall kann man ruhig einmal genauer hingucken. Zusätzlich schränkt const den Einsatz ein, oft verändere ich diese Argumente (s. 2).
    2. Große, kostpielige kopierbare Typen bei denen man eine Kopie in der funktion verändern möchte: Wenn man eine Kopie innerhalb der Funktion braucht um sie lokal zu verändern. const macht dann da absolut keinen Sinn.
    3. in-Parameter: Ähnlich wie 2, die Kopie macht der Compiler beim Aufruf, um sie ins Ziel zu moven, dürfen sie nicht const sein.



  • Arcoth schrieb:

    Nein, und wenn du deine Ansicht nicht änderst, wird mit dir keiner arbeiten wollen. Denn abgesehen von dir findet das so gut wie keiner lesbarer. Du ziehst an den Haaren irgendeinen Firlefanz herbei um dein längst widerlegtes Argument noch über Wasser zu halten.

    lol

    "explizit ist besser als implizit" - und wenn ein einfaches "const" meine Intention als Programmierer ohne irgendwelche Nebenwirkungen verexplizifiziert, wird das von mir genutzt, basta.



  • Nathan schrieb:

    Es gibt drei Anwendungsfälle für by-Value:
    1. Kleine, trivial kopierbare Typen: [...]

    In jedem Fall, wo das Profiling einen zu vernachlässigenden Einfluß auf die Komplexität ergibt, entscheide ich mich für gewöhnlich für by-value. Warum für 0.1% mehr Performance den Code mit Sonderzeichen verunhübschen ? Optimieren kann man nötigenfalls immer noch.



  • großbuchstaben schrieb:

    Nathan schrieb:

    Es gibt drei Anwendungsfälle für by-Value:
    1. Kleine, trivial kopierbare Typen: [...]

    In jedem Fall, wo das Profiling einen zu vernachlässigenden Einfluß auf die Komplexität ergibt, entscheide ich mich für gewöhnlich für by-value. Warum für 0.1% mehr Performance den Code mit Sonderzeichen verunhübschen ? Optimieren kann man nötigenfalls immer noch.

    Lassen wir mal die Performance komplett außen vor:
    Im generischen Code kann man by-Value für pass-Parameter* nicht verwenden, da Objekte ja evtl. nicht kopierbar sind.
    Bei nicht kopierbaren Parametern kann man by-Value ebenfalls nicht verwenden.
    Es bleiben also nur noch nicht generische Funktionen mit kopierbaren Objekten übrig. Das heißt die Regel ist schon einmal nicht einheitlich. Alle nicht arithmetischen Typen als pass-Parameter by-Const-Reference, schon. Wir müssen also mehr nachdenken:
    Ist der Typ kopierbar? Oh, wo war die Datei doch gleich wo der war... Ah, hier. Kopierkonstruktor, Kopierkonstruktor, nein keiner deklariert, aber der hat eine basisklasse. Wo kommt die denn jetzt her? Ist die kopierbar? Nein, auch kein Kopierkonstruktor, die hat aber einen Member x. Ich glaub x, war kopierbar. *compilieren* Verdammt, x war nicht kopierbar.
    Im Gegensatz dazu:
    Ist der Typ char/int/float? Nein, pass-by-Value.
    Und wenn man jetzt die Performance dazu nimmt: Es macht vielleicht nicht viel aus. Aber a) ist das in High-Performance-Code evtl. schon zu viel (Stell dir ein Spiel vor, was in jedem Frame die komplette Liste aller Game-Objekte kopiert!) und b) schadet ein by-Reference nicht (Wenn du keine Sonderzeichen magst, programmier in BASIC!) und man kann es intuitiv ohne nachzudenken hinschreiben.

    *D.h. wird innerhalb der Funktion nicht verändert und auch nirgendwo hinkopiert.


Anmelden zum Antworten