C++ Gurus



  • hauptsache es

    fktiniert



  • Shade Of Mine schrieb:

    Ich verstehe diese einbuchstaben Typen nicht. Denn es ist wirklich nur ein Template Phänomen. Niemand würde

    struct V
    {
      string name;
      int foo;
    };
    

    schreiben. Aber ein

    template<typename V>
    struct Value
    {
      V value;
    };
    

    ist 'OK'.

    Verstehe ich nicht, denn eine logische Argumentation dafür gibt es nicht.

    Ich verstehe dein Problem hierbei nicht wirlich. Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!? Und so direkt vergleichbar ist es mit deinem struct V nicht, von daher ist mir etwas unklar auf welcher Basis du logische Argumente forderst.



  • finix schrieb:

    Ich verstehe dein Problem hierbei nicht wirlich. Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!?

    So kann man bei allem argumentieren.

    Und so direkt vergleichbar ist es mit deinem struct V nicht

    Wieso denn nicht?



  • Michael E. schrieb:

    finix schrieb:

    Ich verstehe dein Problem hierbei nicht wirlich. Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!?

    So kann man bei allem argumentieren.

    Wäre mir neu.

    Michael E. schrieb:

    Und so direkt vergleichbar ist es mit deinem struct V nicht

    Wieso denn nicht?

    Weil es eine völlig andere Situation ist?



  • Stimmt, irgendwie kommt dies häufig bei Templates vor, zumindest soweit ich das bis jetzt sagen kann, soweit reicht meine Erfahrung da noch nicht. Aber wenn ich eine kleine Funktion oder Klasse habe, dann ist da das "T" eigentlich genauso logisch, wie das "i" in einer Schleife. Wenn ich eine große Template-Klasse habe und/oder mit verschiedenen Templatetypen, dann ist es sicherlich übersichtlicher und auch sehr sinnvoll diese entsprechend zu benennen, aber wenn ich nur einen Typen in einem 3-Zeiler habe, dann erfüllt ein "T" denselben Effekt, wie ein "Type" oder was auch immer, jeder weiß, was gemeint ist, wenn ich ein Template mit einem Rückgabe-Typen und zwei Parametertypen beispielsweise habe, dann würde ich "TReturn", "TParam1", "TParam2" evtl. für sinnvoll halten. Bei einer gewissen Mächtigkeit, bei einem 3-Zeiler finde ich "R", "P1", "P2" oder ähnliches ähnlich verständlich und übersichtlich.



  • Wo ist denn das Problem dabei, auch template-Parametern einen guten Namen zu geben? Es hat sich doch schon allgemein die Erkenntnis durchgesetzt, dass sprechende Namen für Instanzen schön sind. Und das sprechende Namen für Typen schön sind. Wenn ich meine Klasse für eine Geldeinheit "Euro" nenne und nicht "E" ist das schöner. Kann man einsehen, oder?
    Jetzt ist der Token "Euro", bzw. "E" doch wirklich nur ein Typname. Völlig egal, was es ist. Ungarische-Notations-Freaks können natürlich T_Euro oder C_Euro schreiben, das ist mir egal. Aber welchen logischen Grund gibt es jetzt, weniger gute Namen für typedefs und Template-Parameter zu nehmen, als für Klassen?

    Also, warum soll es jetzt böse sein, eine Map<KeyType, ValueType> zu haben? Wenn ich Map< schreibe, zeigt mir die IDE schön an, was reinkommt. Natürlich kann ich auch noch K und V interpretieren, ohne in die Hilfe sehen zu müssen. Spätestens beim dritten Parameter wird es aber eklig. Hier wird teilweise so getan, als wäre es ein Weltuntergang, einen vernünftigen Namen zu verwenden. *kopfschüttel*
    Jetzt ist es ja noch nicht mal so, dass ich ständig KeyType und ValueType ausschreiben muss. Wenn ich die Map benutze, setze ich dafür einen konkreten Typ ein und schreibe höchstens diesen aus. Wenn ich die Map programmiere, benenn ich den Parameter halt nach der fertigstellung um, falls es mich wirklich stören sollte, ständig KeyType auszuschreiben. Das sind zwei Mausklicks in einer gescheiten IDE.



  • finix schrieb:

    Michael E. schrieb:

    finix schrieb:

    Ich verstehe dein Problem hierbei nicht wirlich. Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!?

    So kann man bei allem argumentieren.

    Wäre mir neu.

    Was verstehst du denn daran nicht / willst du nicht daran verstehen?

    Michael E. schrieb:

    Und so direkt vergleichbar ist es mit deinem struct V nicht

    Wieso denn nicht?

    Weil es eine völlig andere Situation ist?

    Nö, es wird ein Typname gesucht.



  • Michael E. schrieb:

    finix schrieb:

    Michael E. schrieb:

    finix schrieb:

    Ich verstehe dein Problem hierbei nicht wirlich. Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!?

    So kann man bei allem argumentieren.

    Wäre mir neu.

    Was verstehst du denn daran nicht / willst du nicht daran verstehen?

    Ok, die Langversion, extra für dich: Es wäre mir neu dass man diese Argumentation, in sinnvoller Weise, bei "allem" anwenden könnte.
    Was meinst du, nebenbei, mit "allem"?
    Oder monierst du lediglich dass ich das Problem am konkreten Beispiel nicht entdecken kann?

    Michael E. schrieb:

    Und so direkt vergleichbar ist es mit deinem struct V nicht

    Wieso denn nicht?

    Weil es eine völlig andere Situation ist?

    Nö, es wird ein Typname gesucht.[/quote]
    Es lebe die kontextfreie Simplifikation!! Hurra!

    // Anwendungscode:
    void bar()
    {
      Value<int> v1;
      v1.value = 42;
    
      V v2;
      v2.name = "whatever";
      v2.foo = v1.value;
    }
    

    Siehst du den Unterschied?



  • Ok, die Langversion, extra für dich: Es wäre mir neu dass man diese Argumentation, in sinnvoller Weise, bei "allem" anwenden könnte.
    Was meinst du, nebenbei, mit "allem"?

    "Alles" im Sinne von "jeder Name". Guck dir zum Beispiel dieses Beispiel an:

    template<typename EPdTunewlN, typename ZPdTunewlN>
    EPdTunewwlN EszVdFmewlN(EPdTunewlN EPdFmeaNadTP, ZPdTunewlN ZPdFmeaNadTP)
    {
    	return static_cast<EPdTunewwlN>(EPdFmeaNadTP.ExbMffdmkNme(ZPdFmeaNadTP.EaaybMffdmkNme()));
    }
    

    Legende:

    Erster Parameter des Templates und ein wunderschöner langer Name
    Zweiter Parameter des Templates und ein wunderschöner langer Name
    Eine sinnfreie, zur Veranschaulichung dienende Funktion mit einem wunderschönen langen Namen
    Erster Parameter der Funktion mit einem anderen Namen als die Template-Parameter
    Zweiter Parameter der Funktion mit einem anderen Namen als die Template-Parameter
    Eine x-beliebige Member-Funktion, für die mir kein Name mehr einfällt
    Eine andere, also y-beliebige Member-Funktion, für die mir kein Name mehr einfällt

    Jetzt kann ich auch argumentieren, wenn, ja wenn ich jetzt statt den Abkürzungen sinnvolle Namen benutze, "ist doch offensichtlich was da steht!?" (Zitat von dir).

    Es lebe die kontextfreie Simplifikation!! Hurra!

    Du führst die Simplifikation ein, indem du nur auf den Anwendungscode schaust.



  • Michael E. schrieb:

    Ok, die Langversion, extra für dich: Es wäre mir neu dass man diese Argumentation, in sinnvoller Weise, bei "allem" anwenden könnte.
    Was meinst du, nebenbei, mit "allem"?

    "Alles" im Sinne von "jeder Name". [...]

    Ich gehe einfach mal davon aus dass dein Beispiel ein Scherz sein sollte.

    Michael E. schrieb:

    ... sinnvolle Namen ...

    Was wäre denn in Shades Beispiel ein sinnvoller Bezeichner?

    Michael E. schrieb:

    Es lebe die kontextfreie Simplifikation!! Hurra!

    Du führst die Simplifikation ein, indem du nur auf den Anwendungscode schaust.

    Hm. Bedeutet diese Entgegnung nun dass dir der Unterschied nicht ins Auge gestochen ist oder dass du nicht auf ihn einzugehen vermagst?



  • finix schrieb:

    Michael E. schrieb:

    Ok, die Langversion, extra für dich: Es wäre mir neu dass man diese Argumentation, in sinnvoller Weise, bei "allem" anwenden könnte.
    Was meinst du, nebenbei, mit "allem"?

    "Alles" im Sinne von "jeder Name". [...]

    Ich gehe einfach mal davon aus dass dein Beispiel ein Scherz sein sollte.

    Nein, das war ernst gemeint, frei nach

    finix schrieb:

    Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!?

    Michael E. schrieb:

    ... sinnvolle Namen ...

    Was wäre denn in Shades Beispiel ein sinnvoller Bezeichner?

    Um diese Frage beantworten zu können, braucht man einen Kontext.

    Michael E. schrieb:

    Es lebe die kontextfreie Simplifikation!! Hurra!

    Du führst die Simplifikation ein, indem du nur auf den Anwendungscode schaust.

    Hm. Bedeutet diese Entgegnung nun dass dir der Unterschied nicht ins Auge gestochen ist oder dass du nicht auf ihn einzugehen vermagst?

    Weder noch.

    Gute Nacht.



  • finix schrieb:

    Ich verstehe dein Problem hierbei nicht wirlich. Wenn du aus dem V ein T für Type oder gleich direkt Type machst ist doch offensichtlich was da steht!?

    Darum geht es mir ja. Warum soll ich V schreiben, wenn ich Value oder meinetwegen ValueT oder ValueType schreiben kann?

    Warum soll ich T schreiben, wenn ich Type schreiben kann?

    Warum soll ich K schreiben, wenn ich Key schreiben kann?

    Warum soll ich SP schreiben, wenn ich SmartPointer schreiben kann?

    Warum soll ich A schreiben, wenn ich Allocator schreiben kann?

    Warum soll ich C schreiben, wenn ich Class oder Container schreiben kann?

    PS: zu meinem Beispiel:
    mir geht das darum zu zeigen, dass niemand eine Klasse T, C, V oder sonstwas nennen würde. Weil wir alle gelernt haben: diese Bezeichner sind nicht aussagekräftig. Andererseits ist es gängig T, C, V, A, K, M etc. bei Templates zu verwenden, obwohl hier genauso ordentliche Namen hingehören würden. Denn diese kurzen Bezeichner machen die Templates nur schwerer zu lesen.

    ein

    template<typename I1, typename I2>
    I2 copy(I1 start, I1 end, I2 start2);
    

    ist 'ok'
    aber ein

    template<typename OutIter, typename InIter>
    InIter copy(OutIter start, OutIter end, InIter target);
    

    wäre _viel_ besser.

    Niemand würde ernsthaft eine Klasse I1 nennen oder eine Variable i1 - aber sobald wir bei Templates sind gehört das zum guten Ton?

    'Warum' frage ich. Alles was ich als antwort bekomme ist "kann man doch eh lesen" - aber was ist so schlecht an lesbaren Bezeichnern? Warum nur bei Templates? Was hindert einem daran die paar Zeichen mehr zu schreiben?



  • Da hier ja 2 Elemente beteiligt sind und die gewählten "langen" Namen ja auch wirklich erklärend wirken und somit zur Dokumentation und Erklärung dienen ist diese Form definitiv vorzuziehen

    und das deckt sich mit dem hier schon mehrfach erwähnten Motto
    ➡ So viel wie nötig, so wenig wie möglich.



  • Ich kann Dein Problem an der ganzen Sache nicht wirklich verstehen. Aber wir werden uns hier glaube ich eh ständig im Kreis drehen, ohne uns wirklich einigen zu können, glaube ich.

    Bei Klassen und Funktionen wählt man recht aussagekräftige Bezeichner, soweit ich weiß auch in erster Linie in der Schnittstelle, alles, was klassenintern ist, ist wieder so eine andere Geschichte.

    Das "T" in den Templates ist ja nur ein Platzhalter für "irgendwas", wenn ich eine Templatefunktion habe, dann habe z.B.

    template<typename R, typename T, typename X>R TuWas(T Key, X Value) { ... }
    

    Den Typen bestimme ich, wenn ich das Template aufrufe, und die IDE zeigt mir die Namen der Parameter an, also weiß ich doch trotzdem, welcher Parameter an welcher Stelle wofür steht. Wenn ich

    template<typename ReturnType, typename KeyType, typename ValueType>ReturnType TuWas(KeyType Key, ValueType Value) { ... }
    

    habe, dann halte ich das irgendwie für doppelt gemoppelt. Warum soll ich da unbedingt "ValueType" angeben, wenn es doch durch den Bezeichner klar ist, dass da der Typ des "Values" angegeben wird?

    Zu der i1-Geschichte: Meine PHP-Sources sind voll von Schleifen, wo ich $i1, $i2, etc. verwende. $i1 für die äußerste Schleife, usw. So kann ich jederzeit jede Laufvariable zuweisen. Bei i, j, k, l würde mir dies schwerer fallen.



  • Shade Of Mine schrieb:

    Niemand würde ernsthaft eine Klasse I1 nennen oder eine Variable i1 - aber sobald wir bei Templates sind gehört das zum guten Ton?
    'Warum' frage ich. Alles was ich als antwort bekomme ist "kann man doch eh lesen" - aber was ist so schlecht an lesbaren Bezeichnern? Warum nur bei Templates? Was hindert einem daran die paar Zeichen mehr zu schreiben?

    Auch bei Templates nennst du ja nicht die Klasse T oder sonstwas sondern nur die Templateparameter.
    'Von Außen' ist da nirgends etwas einbuchstabiges zu sehen, nur innerhalb des Templates; dort sollte dann auch leicht ersichtlich wofür es steht (falls es sich nicht ohnehin um eine Lib handelt wo lediglich die Doku ausschlaggebend ist/sein sollte.)

    Das ganze zählt natürlich nur für - im weitesten Sinne - Container-of-T, super-generische Algos oder sonstige 'klare Fälle' ( 🙂 ). Ansonsten zählt natürlich, wie schon zuvor angemerkt und auch nie anders in meinen Beispielcodes zu finden, sinnvolle Bezeichner zu finden und schlicht ein (Konvention) T oder Type anzuhängen, sofern die Natur des Bezeichners (z.B. "fooPolicy") ersichtlich ist.



  • Es gibt kein aussen und innen.
    Es ist alles der Code den man warten muss.

    Nur weil eine Methode private ist, nennen wir sie doch nicht p1 wenn ein stripInvalidSymbols() viel passender wäre, oder?

    Wie nennen wir denn unsere Member Variablen?
    m1, m2?
    Nein, wir nennen sie value, size, speed, fuel,...

    Natürlich ist der Template-Type ein Platzhalter für 'etwas'. Aber dieses 'etwas' hat ja bestimmte Beschränkungen oder einen bestimmten Nutzen/Verwendungszweck.

    Vergleich mal meine 2 copys. Welches ist leichter lesbar?

    Das mit dem ValueType ist wie volkard schon gesagt hat, suboptimal. Viel besser ist ein

    template<typename Result, typename Key, typename Value> Result tuWas(Key key, Value value) { ... }
    

    Denn damit ist sofort klar was tuWas nun liefert. Nämlich Result. Ein R? was wäre R? Hat das R innerhalb der Funktion eine bestimmte Rolle? Ist es nur Result oder etwas Reziprokes? Reversal? Refund? Repeat? Restrict?

    Tut es weh die paar Buchstaben zu schreiben?

    Nehmen wir eine beliebige Funktion (ohne Templates)
    R f(A a)
    cool, was macht die denn? Keine Ahnung.
    jetzt mit Templates:

    template<typename R, typename A>
    R f(A a)
    

    jetzt ist alles klar, stimmts?

    Wie man erkennt, ändert sich die Bedeutung ob mit oder ohne Templates nicht. Ein Typ A sagt mir nix. ein Product, Factor, LoopCondition oder was auch immer hilft mir aber den Sinn sofot zu verstehen.

    Nehmen wir nochmal das schöne copy Beispiel (dass ihr so gerne ignoriert):

    template<typename A, typename B>
    A copy(B b1, B b2, A a);
    

    das ist eure Methode: kurze Bezeichner, weil es ja eh klar ist was sie machen.

    Ich behaupte aber

    template<typename InIter, typename OutIter>
    InIter copy(OutIter start, OutIter end, InIter target);
    

    ist besser lesbar.

    Natürlich kann A nahezu jeder Typ sein, aber lustigerweise macht es nur mit einem Iterator-Typ Sinn.

    Oder nehmen wir

    template<typename T, class A, class C>
    class Stack
    {};
    

    Alles klar?
    Nein?
    Wie wäre es mit:

    template<typename Type, class Allocator, class ContainerImpl>
    class Stack
    {};
    

    jetzt _ist_ es klar.

    Bei Schleifen: i, j, k, l sind (wie ich schon so oft gesagt habe) standardisierte Namen in einem engen eingeschränkten Kontext. Ein i muss immer zu einer Zählschleife gehören. Ein T muss aber nur ein Typ sein. Das ist doch etwas vage... Zumal A und C auch Typen sind, aber welche Rolle spielen sie? Keine Ahnung.
    Bei i und j weiss ich es: ich habe 2 Schleifen: i ist die äußere j die innere. Ich würde zwar nie i1, i2, i3 dafür verwenden, aber das mag auch ok sein. Wichtig ist: ich sehe den Namen und weiss welche Rolle er spielt. Das ist bei T aber _unmöglich_.

    Man lehrt dass sogar den Anfängern: jeder Bezeichner soll so aussagekräftig wie möglich sein. Lieber ein Wort zuviel als zu wenig. Das ist gut so, weil man den Code dadurch leichter lesen kann.
    ein
    template<typename A, typename B, typename C>
    zu lesen ist dagegen schwer.

    Nochmal meine Frage:
    was kostet es, den Bezeichner einen ordentlichen Namen zu geben? Wir machen es doch überall anders auch so, oder? Warum hier nicht?

    'Weil es unnötig ist' ist eine doofe Begründung. Denn es ist auch unnötig lokalen Variablen vernünftige Namen zu geben, ein char b[100] reicht doch statt einem char buffer[100] oder ein S s; statt einem string str; Wozu überhaupt lange Namen verwenden?
    S ist string, also typedef std::string S; V ist vector (dass es auch Value oder Variadic oder Variant etc. heissen kann ignorieren wir natürlich)

    Dann sehen unsere Codes so aus:

    V<C> v;
    v.p('a');
    v.p('b');
    v.p('c');
    S s(v.b(), v.e());
    c<<s;
    

    geil!



  • mantiz schrieb:

    template<typename R, typename T, typename X>R TuWas(T Key, X Value) { ... }
    
    template<typename ReturnType, typename KeyType, typename ValueType>ReturnType TuWas(KeyType Key, ValueType Value) { ... }
    

    Also ich als Anfänger finde die 2. Version wesentlich besser lesbar. Ich hab mir auch selbstprechende Namen (möglichst ohne Ungarische Notation) angewöhnt. Ich finde es manchmal sogar schwieriger die Passenden Namen für Variablen und Funktionen, für ein bestimmtes Problem oder eine bestimmte Klasse, zu finden als das eigentliche Problem zu lösen. Allerdings lohnt sich das (bei mir) auch immer da ich auf diese Weise Komplexere Algorithmen wirklich besser lesen kann. Es ist einach nur schön nicht die Maus über eine Variable halten zu müssen um den Variablentyp zu sehen. Hab mir auch schon so tolle (aber meiner Meinung nach Aussagekräfige) Namen wie:

    int GroesseDesCStringsInByte; //irgendein Kommentar der kurz die Verwendung erklärt
    int ZeilenAnzahl;
    

    einfallen lassen. Naja mir hats bisher immer geholfen die Lösung meines Problems "passiv" zu erklären. Das mach ich übrigens auch bei kleinen, lästigen und kurzen Funktionen.

    PS: weis einer von euch überhaupt noch worum es in desem Thread geht? OHNE nochmal den ersten Beitrag zu lesen? 😃



  • @Chris++:
    Meine Variante wäre hier:

    int Length; // in Bytes; Verwendung
    int RowCount;
    

    dürfte nicht weiter zu Missverständnissen führen, da man die Variablen ja sowieso in einem möglichst kleinen Kontext/Block definiert, wo man sie braucht. Englische Bezeichner sind häufig kürzer, als deutsche und genauso aussagekräftig, finde ich.

    Ein Beispiel evtl. noch, wenn ich eine Funktion MoveResized beispielsweise habe, dann würde ich die so definieren:

    void MoveResized(int x, int y, int w, int h);
    

    Man könnte auch:

    void MoveResized(int Left, int Top, int Width, int Height);
    

    nehmen, was aber meiner Meinung nach überhaupt keinen Vorteil bringt. Man weiß, dass x und y die obere linke Ecke angeben, und im Kontext vom in der Größe verändernden verschieben sollte es auch klar sein, wofür w und h stehen. Wenn ich diese Funktion in der WinAPI für Fenster implementiere, dann rufe ich genau eine Funktion mit diesen Parametern auf:

    void MoveResized(int x, int y, int w, int h) {
        ::MoveWindow(x, y, w, h, true);
    }
    

    Wofür ausschreiben, ist doch klar, was da passiert?

    In diesem Beispiel wäre Top evtl. sogar schon fast grob fahrlässig, denn man könnte ja evtl. auch das Koordinatensystem ändern, wo x und y auf einmal die untere linke Ecke angeben und schon stimmen die schönen selbsterklärenden Bezeichner nicht mehr.

    @copy-Beispiel:
    Wenn nur ein Iterator Sinn macht, geht dann nicht sowas in der Art?

    template<typename T1, typename T2>
    vector<T1>::iterator copy(vector<T2>::iterator itStart, vector<T2>::iterator itEnd, vector<T1>::iterator itTarget);
    

    Bin mir nicht sicher, ob das so funktioniert, aber von der Art her sollte klar sein, was ich meine. 🙂
    Vectoren, Maps, etc. müssten u.U. sowieso unterschiedlich behandelt werden und so hätte man eine gewisse Typsicherheit, denke ich, oder?



  • Chris++ schrieb:

    Ich finde es manchmal sogar schwieriger die Passenden Namen für Variablen und Funktionen, für ein bestimmtes Problem oder eine bestimmte Klasse, zu finden als das eigentliche Problem zu lösen.

    Dann bist schon ziemlich weit. Denn normalerweise erreichen die meisten Leute die Hürde garnicht...

    Ich verwende auch oft 30% der Zeit für Namensfindung. Manchmal sogar mehr. Es zahlt sich einfach aus - wenn ich an meine Zeit an dt2 denke -> urghs, data_save, data_session_save, data_read und die meiste Zeit wurde nix ge'read'et oder ge'save'et...



  • Seit ich bei der Ferialarbeit letztes Jahr mit einem sehr großem Softwareprojekt zu tun hatte sehe ich ein das sich solche Arbeit auszahlt. Ich konnte es sehr lange nicht glauben warum man für jede Methode einen JavaDoc-Kommentar schreiben soll. Jetzt weiß ich es. 2000 Quellcodedateien lassen nicht nur leicht sondern äußerst leicht den Überblick verlieren. Und eine Klasse ohne Doku steht da ganz schnell ohne Benutzer da, weil der Autor die Firma irgendwann verlassen hat.

    Auch wird sehr viel Aufwand für JUnit-Tests aufgewendet - auch ein Ding der Unvorstellbarkeit - bei mir vor einigen Jahren.

    @mantiz: Top, Left sind gute Variablennamen - allerdings nur wenn sie mit kleinem Anfangsbuchstaben beginnen 😉

    template<typename T1, typename T2>
    vector<T1>::iterator copy(vector<T2>::iterator itStart, vector<T2>::iterator itEnd, vector<T1>::iterator itTarget);
    

    Solch Code schockt mich immer, da verspüre ich zuerst den Drang nach einem typedef, danach den Drang nach einer anderen Programmiersprache. Also wenn sich in C++ was ändern soll dann Templates.

    MfG SideWinder


Anmelden zum Antworten