Welche Sprachfeatures sollte man vermeiden?



  • Welche Sprachfeatures und Befehle sollte man im modernen (oder gar postmodernen) C++ vermeiden und aus welchen Gründen?

    Einige mögliche Punkte, führe ich schon mal an. Stimmt das so?

    • alte Raw Pointer
    • DEFINE für Konstanten
    • friend Funktionen
    • Singleton Pattern
    • Functors (besser Lamda benutzen)
    • alte ENUMS, unscoped ENUMS (besser Class enums)
    • Inline


  • @Mond sagte in Welche Sprachfeatures sollte man vermeiden?:

    Welche Sprachfeatures und Befehle sollte man im modernen (oder gar postmodernen) C++ vermeiden und aus welchen Gründen?

    Einige mögliche Punkte, führe ich schon mal an. Stimmt das so?

    Nein. So allgemein stimmt das nicht. Bis auf:

    • DEFINE für Konstanten

    Das würde ich unterschreiben.

    Aber zum Beispiel hier:

    • alte Raw Pointer

    Es geht nur um besitzende Raw Pointer!

    Könntest du für die anderen Punkte genauer eine Begründung ausführen?



  • Singleton Pattern hat ja nichts mit C++ zu tun, ist auch kein Sprachfeature. Auch warum man es vermeiden sollte verstehe ich nicht. An den richtigen Stellen eingesetzt kann es sich als Sinnvoll erweisen, nur wird es oftmals falsch eingesetzt um schlechtes Design zu kompensieren.

    Auch warum Inline vermieden werden sollte erschließt sich mir nicht, wenn das so ist, bitte ich gernen um einen Erklärung, mir adhoc nichts einfallen was gegen inline spricht.



  • Vielleicht habe ich mich falsch ausgedrückt. Meine Liste war eher eine Liste an Fragen, weniger Feststellungen. Dennoch habe ich einige mögliche Begründungen in einem Edit hinzugefügt.

    @wob sagte in Welche Sprachfeatures sollte man vermeiden?:

    Es geht nur um besitzende Raw Pointer!

    Was gibt es denn sonst noch für weitere Raw Ponter?


  • Mod

    Da würde ich nur die ersten beiden direkt unterschreiben, und das erste auch nicht absolut. Vielleicht noch das mit den Enums.

    • rohe Pointer -> Es gibt (fast) immer ein neues Äquivalent, das strikt besser ist. Für den klassischen Einsatzzweck als indirekter Verweis, sind sie aber legitim.
    • DEFINE für Konstanten -> Es gibt immer ein neues Äquivalent, das strikt besser ist
    • friend -> eigentlich legitimes Mittel, wird aber fast immer nur aus Faulheit, Unwissen, oder mangelhaftem Design missbraucht. Wenn gut eingesetzt, dann spricht nichts dagegen. Da es insgesamt sowieso wenig genutzt wird, ist es sicher auch nicht so wichtig, groß darüber zu diskutieren. Wenn man doch viel friend in seinem Code hat, ist das ein Code Smell, denn man fällt dann fast sicher in die Kategorie Faulheit, Unwissen, mangelhaftes Design.
    • Singleton -> Ähnlich wie friend. Gerüchtehalber ist das Verhältnis von Faulheit, Unwissen, mangelhaftem Design zu legitimen Nutzen hier besonders schlimm, aber zumindest in meiner praktischen Erfahrung sahen die wenigen Singletons die ich gesehen habe, auch halbwegs legitim aus. Vielleicht hat sich ausreichend herumgesprochen, dass zu viele Singletons ein Smell sind.
    • Functors -> Warum nicht?
    • alte enums -> Fallen in die Richtung von rohen Zeigern, aber mit weniger praktischen Problemen, und allgemein geringer verbreiteter Nutzung. Aber eigentlich gibt es keinen legitimen Grund, sie noch zu nutzen.
    • inline -> WTF? Eines der wichtigsten Sprachfeatures, ohne das man kaum auskommt. Dir ist klar, dass Funktionen, die in der Klassendefinition definiert werden, automatisch inline sind und dass das ungeheuer wichtig ist?


  • @SeppJ sagte in Welche Sprachfeatures sollte man vermeiden?:

    • inline -> WTF? Eines der wichtigsten Sprachfeatures, ohne das man kaum auskommt. Dir ist klar, dass Funktionen, die in der Klassendefinition definiert werden, automatisch inline sind und dass das ungeheuer wichtig ist?

    Nein, ist mir nicht klar, danke für den Hinweis.
    Zum Inline dennoch:
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1604r0.html



  • @Mond sagte in Welche Sprachfeatures sollte man vermeiden?:

    @SeppJ sagte in Welche Sprachfeatures sollte man vermeiden?:

    • inline -> WTF? Eines der wichtigsten Sprachfeatures, ohne das man kaum auskommt. Dir ist klar, dass Funktionen, die in der Klassendefinition definiert werden, automatisch inline sind und dass das ungeheuer wichtig ist?

    Nein, ist mir nicht klar, danke für den Hinweis.
    Zum Inline dennoch:
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1604r0.html

    Da steht nicht, dass du inline vermeiden sollst.



  • @Mond
    Du hast goto vergessen. Ein Mittel was man heutzutage überhaupt nicht mehr benötigt, aber manchmal noch sieht und Spagetti-Code vom Feinsten produziert!



  • @Quiche-Lorraine sagte in Welche Sprachfeatures sollte man vermeiden?:

    Du hast goto vergessen. Ein Mittel was man heutzutage überhaupt nicht mehr benötigt

    Ausser in C - dort ist findet man es meistens in einer sinnvollen Anwendung: diesen "handgeschriebenen Destruktoren" am Funktionsende... goto cleanup, goto fail. Braucht man in C++ aber wirklich nicht mehr. Ausser vielleicht diesem Ding, wenn man aus mehrfach verschachtelten Schleifen herausspringen will - sowas löse ich aber meist mit return.



  • @Mond sagte in Welche Sprachfeatures sollte man vermeiden?:

    • Functors (besser Lamda benutzen)

    Das macht keinen Sinn. Labdas sind praktisch functors.



  • @SeppJ sagte in Welche Sprachfeatures sollte man vermeiden?:

    • friend -> eigentlich legitimes Mittel, wird aber fast immer nur aus Faulheit, Unwissen, oder mangelhaftem Design missbraucht. Wenn gut eingesetzt, dann spricht nichts dagegen. Da es insgesamt sowieso wenig genutzt wird, ist es sicher auch nicht so wichtig, groß darüber zu diskutieren. Wenn man doch viel friend in seinem Code hat, ist das ein Code Smell, denn man fällt dann fast sicher in die Kategorie Faulheit, Unwissen, mangelhaftes Design.

    Das ist der einzig sinnvolle Weg, um Operatoren wie operator* für z.B. Matrixklassen zu definieren. Wenn man sie nicht friend deklariert, dann braucht man ein Interface in der Klasse, die diese Operation nach außen zur Verfügung stellt, und dann trotzdem den externen freien operator*, weil sonst die Typpromotion im Kontext dieser Klasse nicht funktioniert.



  • @john-0 sagte in Welche Sprachfeatures sollte man vermeiden?:

    Das ist der einzig sinnvolle Weg, um Operatoren wie operator* für z.B. Matrixklassen zu definieren. Wenn man sie nicht friend deklariert, dann braucht man ein Interface in der Klasse, die diese Operation nach außen zur Verfügung stellt, und dann trotzdem den externen freien operator*, weil sonst die Typpromotion im Kontext dieser Klasse nicht funktioniert.

    Das kann ich nicht ganz nachvollziehen. Mein freier Multiplikationsoperator greift via public: T& operator(i, j) auf die Elemente zu und für besondere (z.B. SIMD-) Optimierungen stellt die Matrix-Klasse auch noch eine Member-Funktion public: T* Matrix::elements() zur Verfügung, wenn man linearen Zugriff auf den Speicher benötigt. friendist hier auch eine Lösung, aber sicher nicht die "einzig sinnvolle".



  • @SeppJ sagte in Welche Sprachfeatures sollte man vermeiden?:

    alte enums -> Fallen in die Richtung von rohen Zeigern, aber mit weniger praktischen Problemen, und allgemein geringer verbreiteter Nutzung. Aber eigentlich gibt es keinen legitimen Grund, sie noch zu nutzen.

    Naja was ist, wenn du das enum als Bitmaske verwenden möchtest, dann müsstest du vorher noch jeden einzelnen Operator dafür definieren und das dann auch noch pro Typ.

    Desöfteren benutze ich auch ein plain old enum innerhalb eines Klassen-Scopes. Somit ist eine Namenskollision gelindert und man kann sie gleich wie richtig nette und hundsgewöhnliche Integer behandeln.



  • @Finnegan sagte in Welche Sprachfeatures sollte man vermeiden?:

    Das kann ich nicht ganz nachvollziehen. Mein freier Multiplikationsoperator greift via public: T& operator(i, j) auf die Elemente zu und für besondere (z.B. SIMD-) Optimierungen stellt die Matrix-Klasse auch noch eine Member-Funktion public: T* Matrix::elements() zur Verfügung, wenn man linearen Zugriff auf den Speicher benötigt. friendist hier auch eine Lösung, aber sicher nicht die "einzig sinnvolle".

    Man will solche Details wie die interne Datenstruktur gar nicht nach außen sichtbar machen. Das ist ein internes Detail. Dazu braucht man für eine sinnvolle Implementation auch noch mehr Informationen als den reinen rohen Zeiger auf das Feld. Wie sind die Dimensionen, wie sind die Daten gespeichert (row-major- oder column-major-order), gibt es eine leading dimension, packed vs. nonpacked, … . Diese Dinge braucht man spätestens dann, wenn man z.B. eine hochoptimierte Version der BLAS S/D/C/ZGEMM routine anstatt der selbsgeschriebenen Routine nutzen will/muss.



  • @john-0 sagte in Welche Sprachfeatures sollte man vermeiden?:

    @Finnegan sagte in Welche Sprachfeatures sollte man vermeiden?:

    Das kann ich nicht ganz nachvollziehen. Mein freier Multiplikationsoperator greift via public: T& operator(i, j) auf die Elemente zu und für besondere (z.B. SIMD-) Optimierungen stellt die Matrix-Klasse auch noch eine Member-Funktion public: T* Matrix::elements() zur Verfügung, wenn man linearen Zugriff auf den Speicher benötigt. friendist hier auch eine Lösung, aber sicher nicht die "einzig sinnvolle".

    Man will solche Details wie die interne Datenstruktur gar nicht nach außen sichtbar machen. Das ist ein internes Detail. Dazu braucht man für eine sinnvolle Implementation auch noch mehr Informationen als den reinen rohen Zeiger auf das Feld. Wie sind die Dimensionen, wie sind die Daten gespeichert (row-major- oder column-major-order), gibt es eine leading dimension, packed vs. nonpacked,

    Ja, ich verstehe den Gedanken dahinter, ich habe da bei meiner Matrix-Klasse eine anderes Designziel: Ich sehe diese vornehmlich als ein mathematisches Array wie einen std::vector, der ja auch mit v.data() einen direkten Zugriff auf seine Daten erlaubt. Bei mir sind sogar matrix.element_order() (Row/Column-Major) und andere public-Member-Funktionen. Diese nicht-mathematischen Interna sollen auch nicht vor dem Anwender versteckt werden, da die Bibliothek keine abgeschlosene Einheit bildet, sondern vornehmlich mit anderen Bibliotheken und Grafik-APIs interagieren soll.

    Es handelt sich um eine Matrix-Klasse für statische Matrizen fester Größe für die Grafikprogrammierung (analog zu std::array). Matrizen und Arrays dieser Matrizen sollen hier unmodifiziert z.B. an OpenGL/Direct3D/Vulkan, Physik-Engines oder irgendwelche Bibliotheken für Grafik- und Geometriealgorithmen weitergereicht werden. Dafür ist es hier auch für den Anwender wichtig zu wissen, wie die Daten intern organisiert sind und dass er auf diese auch direkt zugreifen kann (z.B. ein std::vector<Matrix>::data() per API-Call direkt in den GPU-Speicher pumpen) .

    Nichts gegen friend - das ist sicher eine gute Lösung für deinen Anwendungsfall. Es ist nur eben nicht die "einzig sinnvolle" für eine Matrixklasse. Das hängt eben auch stark vom Anwendungsgebiet und den Designzielen ab.


Anmelden zum Antworten