Sprachmittel, die ihr nie benutzt



  • Bashar schrieb:

    Das ist soweit eigentlich OK, es ist wirklich die Kommutativität von +, die nicht immer sinnvoll ist.

    Wo sonst? Und findest du die Nicht-Kommutativität bei std::string sinnvoll?



  • Nexus schrieb:

    Bashar schrieb:

    Das ist soweit eigentlich OK, es ist wirklich die Kommutativität von +, die nicht immer sinnvoll ist.

    Wo sonst? Und findest du die Nicht-Kommutativität bei std::string sinnvoll?

    Da war die Wahl de Zeichens '+' eine unglückliche Wahl.



  • Nexus schrieb:

    Bashar schrieb:

    Das ist soweit eigentlich OK, es ist wirklich die Kommutativität von +, die nicht immer sinnvoll ist.

    Wo sonst? Und findest du die Nicht-Kommutativität bei std::string sinnvoll?

    Ja klar, warum nicht?



  • volkard schrieb:

    Nexus schrieb:

    Wo sonst? Und findest du die Nicht-Kommutativität bei std::string sinnvoll?

    Da war die Wahl de Zeichens '+' eine unglückliche Wahl.

    Das ist Geschmackssache, ich finde es jedenfalls durchaus intuitiv bei der Anwendung.



  • asc schrieb:

    Das ist Geschmackssache, ich finde es jedenfalls durchaus intuitiv bei der Anwendung.

    Ich finde . und & auch nicht schlecht, in Basic-Dialekten, wo + und & gehen, bevorzuge ich &.


  • Mod

    Hier auch mal mein Senf, falls es jemandem interessiert:
    Sprachmittel, die ich nie einsetze:

    • Exception-Spezifikationen
    • variable Argumentlisten
    • Speicherklassen-Schlüsselwörter register und auto (in seiner alten Bedeutung)
    • Pointer-to-Member (nicht Memberfunktionszeiger) (nur mal versehentlich, als ich einem Compilerfehler begegnet bin)
    • Function-Style- und C-Casts
    • Bitfelder
    • Digraphen, Trigraphen (man beachte, dass ich die alternativen Schlüsselwörter ausdrücklich benutze!)
    • export
    • eigene new - und delete -Operatoren
    • dynamic_cast
    • union
    • goto , Labels
    • volatile
    • Inline-Assembler
    • wchar_t , wide Strings & Co.
    • Lokale Klassen (d.h. Klassen in Funktionen, nicht Klassen in Klassen)
    • operator->* , operator~
    • Überladung von: operator->* , operator& , operator~ , operator+ (unär), logische Verknüpfungsoperatoren.
    • Präprozessordirektiven: #line, #error, Nulldirektive 😃 , im Standard vordefinierte Makros (die sind total nutzlos!)

    Sehr selten:

    • virtuelle Vererbung
    • Funktionstemplate-Spezialisierung
    • const_cast
    • Arrays
    • switch Ja, kaum zu glauben, aber es kommt einfach nie vor, außer bei Hausaufgaben im Forum.
    • mutable
    • explicit Auch kaum zu glauben. Aber meine Konstruktoren haben irgendwie immer entweder 2+-Argumente oder sollen tatsächlich implizit sein.
    • float, short, signed char, long double
    • continue
    • Überladung von: operator, , operator- (unär)

    Das ist meine Liste nach Durchgehen der Beiträge hier im Thread und Lesen der Überschriften im Standard bis Kapitel 17 (wo die Sprachbibliothek losgeht).



  • Ich kann im Wesentlichen Nexus zustimmen. Kleine Unterschiede/Ergänzungen:

    - Pointer-to-Member: Kommen hin- und wieder mal vor in Verbindung mit bind.
    - Spezialisierung von Funktions-Templates: Nie.
    - volatile: Nie.
    - Polymorphe Basisklassen sind immer abstrakt bei mir.



  • SeppJ, du hast noch einiges aufgezählt, das ich auch nie verwende. Z.B. Inline-Assembler, funktionslokale Klassen (deren Schicksal ist mit Lambdas eh besiegelt) und #line . Operatoren und Typen verwende ich auch nicht alle. Und die Nulldirektive kannte ich gar nicht, muss ich von nun an öfter einsetzen 🤡

    Aber wenn ich den C++-Standard kürzen müsste, würde ich wahrscheinlich mit all den alternativen Schreibweisen anfangen (besonders Di/Trigraphen benutzt eh niemand, und für die Schlüsselwörter reichen auch Makros :p). Exception-Spezifikationen sind wenigstens bald deprecated, export wird ganz entfernt. Bei VarArgs oder register wirds wohl wegen C-Kompatibilität noch ewig dauern, falls die überhaupt jemals rausfliegen. Der Rest sollte okay sein, auch wenn ich persönlich nicht Gebrauch davon mache.


  • Mod

    Nexus schrieb:

    Und die Nulldirektive kannte ich gar nicht, muss ich von nun an öfter einsetzen 🤡

    Ja, die war mir auch neu, ich bin nur im Standard drüber gestolpert und musst erst einmal lesen, was das überhaupt ist.



  • Nexus schrieb:

    Exception-Spezifikationen sind wenigstens bald deprecated, export wird ganz entfernt.

    Good point. Aber wieso ist im neuen Standard noexcept drin? Tut es nicht genau das selbe?



  • exceptthis schrieb:

    Good point. Aber wieso ist im neuen Standard noexcept drin? Tut es nicht genau das selbe?

    Nein, noexcept ist schon sinnvoll, um Funktionen mit Nothrow-Garantie kennzuzeichnen.

    Es gab hier schon ein paar Threads dazu, da könntest du Genaueres darüber finden. Z.B. ab hier (ist etwas viel Noise, beachte vor allem die Beiträge von krümelkacker und Shade Of Mine).



  • Nexus schrieb:

    SeppJ, du hast noch einiges aufgezählt, das ich auch nie verwende. Z.B. Inline-Assembler, funktionslokale Klassen (deren Schicksal ist mit Lambdas eh besiegelt)

    Sie sind erst mit dem C++0x Standard wirklich einsetzbar. Vorher konnten sie ja keine Templateargumente sein. Und für alles, was nur ein klein wenig komplizierter ist, sind lambdas eh unübersichtlich und funktionslokale Klassen auf einmal weit vorne.

    Ansonstne kann ich mich SeppJs Liste ganz gut anschließen. Mutable kommt bei mir momentan leider ein wenig zu häufig vor und dynamic_cast brauche ich leider auch ab und zu, da ich templates und abstrakte Interfaces unter einen Hut kriegen muss.

    und natürlich für sowas:

    Base* obj=...;
    OptimizedDerived* objOpt = dynamic_cast<OptimizedDerived*>(obj);
    if(objOpt){
        kernel=createOptimizedKernel(objOpt);
    }
    else
    {
        kernel=createStandardKernel(obj);
    }
    

    wenn mir jemand sagen kann, wie ich sowas ohne dynamic_cast lösen könnte, wäre ich dankbar. Ohne Abstrakte Basisklassen ist das ja trivial.



  • otze schrieb:

    Sie sind erst mit dem C++0x Standard wirklich einsetzbar. Vorher konnten sie ja keine Templateargumente sein. Und für alles, was nur ein klein wenig komplizierter ist, sind lambdas eh unübersichtlich und funktionslokale Klassen auf einmal weit vorne.

    Wirklich? Ich kann mir nicht vorstellen, die jemals gross einzusetzen. Sobald etwas ein wenig komplizierter wird, kommt es bei mir eh aus der Funktion raus in einen detail -Namensraum. Lambdas hingegen kann man schön für triviale Funktoren benutzen.

    otze schrieb:

    wenn mir jemand sagen kann, wie ich sowas ohne dynamic_cast lösen könnte, wäre ich dankbar. Ohne Abstrakte Basisklassen ist das ja trivial.

    Eine virtuelle Funktion in Base und OptimizedDerived geht nicht (z.B. wegen festgelegter Klassen)?

    Base* obj = ...;
    kernel = obj->createKernel();
    

  • Mod

    otze schrieb:

    und natürlich für sowas:

    Base* obj=...;
    OptimizedDerived* objOpt = dynamic_cast<OptimizedDerived*>(obj);
    if(objOpt){
        kernel=createOptimizedKernel(objOpt);
    }
    else
    {
        kernel=createStandardKernel(obj);
    }
    

    wenn mir jemand sagen kann, wie ich sowas ohne dynamic_cast lösen könnte, wäre ich dankbar. Ohne Abstrakte Basisklassen ist das ja trivial.

    Wäre das nicht einfach durch eine entsprechende virtuelle Funktion zu lösen?



  • Was Memberzeiger angeht:

    template<typename T, typename D, typename F = std::plus<D> >
    class member_accumulation_functor {
    public:
      inline explicit member_accumulation_functor(D T::*p, F const &f = F())
        : ptr_(p), func_(f) {}
    
      inline D operator()(D const &prev, T const &x) {
        return func_(prev, x.*ptr_);
      }
    
    private:
      D T::*ptr_;
      F func_;
    };
    
    template<typename T, typename D, typename F>
    inline member_accumulation_functor<T, D, F> member_accumulator(D T::*p, F const &f) {
      return member_accumulation_functor<T, D, F>(p, f);
    }
    
    template<typename T, typename D>
    inline member_accumulation_functor<T, D> member_accumulator(D T::*p) {
      return member_accumulation_functor<T, D>(p);
    }
    
    ...
    
    struct foo {
      double x, y, z;
    } foo_array[10];
    
    double sum = std::accumulate(foo_array, foo_array + 10, member_accumulator(&foo::y));
    

    Jetzt kann man natürlich argumentieren, dass das sauberer mit get-Methoden x(), y() und z() gelöst wäre, aber mal unter uns Pastorentöchtern, im namespace { } benutzt man der Schnelle halber halt doch mal nackte Structs, und gelegentlich hat man ja auch mit C-APIs oder generell nackten Structs anderer Leute zu tun. Es ist nicht besonders häufig, dass man so etwas braucht, aber gelegentlich doch ganz praktisch zu haben.

    Was ich nicht (mehr) einsetze, weil es meiner Erfahrung nach unnötig Flexibilität entfernt, sind Templates als Template-Argumente, etwa

    template<typename T, template<typename> class U> class foo { U<T> bar; };
    
    foo<int, my_template> baz;
    

    Das ist meiner Meinung nach besser als

    template<typename T> class foo { T bar; }
    
    foo<my_template<int> > baz;
    

    geschrieben. Kommt auch selten als Pflichtparameter vor, und die Standardbibliothek hält es beispielsweise mit Vergleichsfunktionen ähnlich.



  • camper schrieb:

    Wäre das nicht einfach durch eine entsprechende virtuelle Funktion zu lösen?

    Kann man machen. Aber das is thalt nur ein einziger mathematischer Sonderfall (der recht wichtig ist). Und da wollen wir eigentlich kein abstraktes Interface mit zu müllen. Es gibt zwar mehr Sonderfälle, aber jede braucht ihre eigene handgedengelte Lösung.



  • @ seldon: Ich verwende Template-Template-Parameter hauptsächlich bei Policy-Based-Design. Und da ich die Problemstellung des Akkumulators in der Form recht selten habe, hätte ich wohl eine guten alte Schleife genommen 😉

    @ otze: Oder du nimmst einen Dispatcher, der irgendeine Typ-/Kategorie-ID auf Funktionen abbildet. Prinzipiell eine std::map<std::type_info, std::function<void(Base*)>, comp> , eventuell auch was Anderes als type_info . Ist besonders praktisch, um virtuelle Funktionen in Klassen zu simulieren, deren Interface man nicht antasten kann/will. Ich hätte auch eine fertige Implementierung dazu.

    typeid hat allerdings zwei Probleme: Vergleiche (und dadurch dispatchte Aufrufe) sind recht langsam. dynamic_cast allerdings auch. Ausserdem erkennst du keine Derived-To-Base-Konvertierungen, eine von OptimizedDerived abgeleitete Klasse müsste also separat beim Dispatcher registriert werden. Von daher könnte sich unter Umständen eine Funktion Base::GetID() bewähren, falls du mehrere Klassen gleich behandeln willst.

    Doch ob sich das in deinem Fall lohnt, kann ich nicht sagen. Virtuelle Funktionen wären wohl das Naheliegendste. Aber ab und zu sind unkonventionelle Lösungen ganz nützlich.



  • SeppJ schrieb:

    Hier auch mal mein Senf, falls es jemandem interessiert:
    Sprachmittel, die ich nie einsetze:

    • Exception-Spezifikationen true
    • variable Argumentlisten Nur in Makros, ansonsten variadic templates 😉
    • Speicherklassen-Schlüsselwörter register und auto (in seiner alten Bedeutung) true
    • Pointer-to-Member (nicht Memberfunktionszeiger) (nur mal versehentlich, als ich einem Compilerfehler begegnet bin) wow, kannte ich gar nicht
    • Function-Style- und C-Casts true, was ist eigentlich der Unterschied zwischen einem Function-Style-Cast und einem Konstruktoraufruf?
    • Bitfelder true
    • Digraphen, Trigraphen true
    • export true
    • eigene new - und delete -Operatoren true, außer zum Debuggen, bevor ich smart-Pointer entdeckt habe
    • dynamic_cast true, außer in altem Code mit schlechtem Design
    • union true
    • goto , Labels true
    • volatile true, außer bei Mikroprozessor- und evtl. Multithread-Programmen (welche ich im Moment nicht schreibe)
    • Inline-Assembler
    • wchar_t , wide Strings & Co. verwende ich durchaus, allerdings nicht mit wchar_t, da wchar_t manchmal nur 2 Byte breit ist und das dann nur halbes Unicode wäre
    • Lokale Klassen (d.h. Klassen in Funktionen, nicht Klassen in Klassen) true
    • operator->* , operator~ sehr spezifisch, ~ z.B. für Bit-Frickling auf dem Mikroprozessor, -> selbst überladen nicht aber für den Aufruf von Memberfunktionen erst gestern verwendet (hast du operator.* vergessen?)*
    • Überladung von: operator->* , operator& , operator~ , operator+ (unär), logische Verknüpfungsoperatoren. true
    • Präprozessordirektiven: #line, #error, Nulldirektive 😃 , im Standard vordefinierte Makros (die sind total nutzlos!)

    #line und Nulldirektive kannte ich gar nicht (Nulldirektive zumindest nicht als solchese Wort, nur als "Designzeichen"); #error für Plattform-Fehler; __FILE__, __LINE__, __DATE__ und __TIME__ evtl. zu Debuggingzwecken
    Sehr selten:

    • virtuelle Vererbung true + protected-/private-Vererbung
    • Funktionstemplate-Spezialisierung momentan nicht
    • const_cast true
    • Arrays true, dazu gibts ja (seit kurzem/bald, je nach Compiler) std::array
    • switch Ja, kaum zu glauben, aber es kommt einfach nie vor, außer bei Hausaufgaben im Forum. true
    • mutable true, kannte ich noch gar nicht
    • explicit Auch kaum zu glauben. Aber meine Konstruktoren haben irgendwie immer entweder 2+-Argumente oder sollen tatsächlich implizit sein. true
    • float, short, signed char, long double true
    • continue selten
    • Überladung von: operator, , operator- (unär) Komma-Operator nicht, unäres Minus schon (was spricht dagegen?)

    Auch sehr selten bis gar nicht: RTTI


  • Mod

    wxSkip schrieb:

    virtuelle Vererbung true + protected-/private-Vererbung

    Oh, die habe ich noch vergessen. Fallen unter nie.

    [*]Überladung von: operator, , operator- (unär) Komma-Operator nicht, unäres Minus schon (was spricht dagegen?)

    Es spricht (im Gegensatz zum Komma) nichts dagegen, unäres Minus zu überladen, ich brauchs nur nie.



  • SeppJ schrieb:

    Es spricht (im Gegensatz zum Komma) nichts dagegen, unäres Minus zu überladen, ich brauchs nur nie.

    Aber operator* und operator/ brauchst du?


Anmelden zum Antworten