const string * == const string * const



  • Warum compiliert das nicht?

    void foo( const string * str)
    {
    }
    
    void foo( const string * const str)
    {
    }
    
    int main() {
    
      return 0;
    }
    

    prog.cpp: In function ‘void foo(const string*)’:
    prog.cpp:9:6: error: redefinition of ‘void foo(const string*)’
    void foo( const string * const str)
    ^
    prog.cpp:5:6: error: ‘void foo(const string*)’ previously defined here
    void foo( const string * str)
    ^

    http://ideone.com/XpG9NI

    Das sind doch unterschiedliche Sachen http://www.codeguru.com/cpp/cpp/cpp_mfc/general/article.php/c6967/Constant-Pointers-and-Pointers-to-Constants.htm



  • Das sind nicht wirklich unterschiedliche Sachen. Das ist wie:

    void foo(int);
    void foo(const int);
    
    foo(42); //welches foo?
    

    Da du eine Kopie der Variablen machst ist die Kopie nicht mehr const. Daher kann foo(const int) niemals aufgerufen werden und der Standard meint man soll lieber einen Kompilierfehler bekommen statt es den Compiler schlucken zu lassen und ewig so einen Fehler suchen zu müssen. Wenn du stattdessen Referenzen nimmst ist es eindeutig.

    void foo(int &);
    void foo(const int &);
    
    foo(42); //muss ans zweite binden
    
    void foo( const string * &str)
    {
    }
    
    void foo( const string * const &str)
    {
    }
    
    int main(){
    } //kompiliert
    


  • Top-Level-CV-Qualifizierer bei Parametern gehören nicht zur Signatur. Du definierst also zwei Mal die gleiche Funktion.

    Falls du damit eine Überladung bezweckst, überleg doch einfach, wie die Funktion ausgewählt werden soll -- es geht nicht.

    Ohnehin sind Top-Level-CV-Qualifizierer bei Rückgabetypen und Parametern fragwürdig, da sie lediglich aussagen, dass eine Kopie nicht verändert wird, was den Aufrufer nicht interessiert. Bei Parametern kann das const höchstens in der Deklaration sinnvoll sein, um versehentliche Änderungen in der Implementierung zu vermeiden.



  • Nexus schrieb:

    Ohnehin sind Top-Level-CV-Qualifizierer bei Rückgabetypen und Parametern fragwürdig, da sie lediglich aussagen, dass eine Kopie nicht verändert wird, was den Aufrufer nicht interessiert.

    Das ist eine Frage des persönlichen Stils, würde ich sagen.

    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).

    Ob er dann pass-by-value -- und damit das const eig überflüssig -- ist oder by pointer oder by reference, interessiert mich erst auf den zweiten Blick, bspw wenn es um Effizienz oder Optimierung geht.



  • 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.


Log in to reply