Array übergeben zu Konstruktor



  • @SeppJ

    Klar fällt mir was auf. Sind deine Datenstrukturen alle als Templates definiert, wo du den Containertyp als Template Parameter festlegst? Bei irgendeiner bekloppten Lotterie könnten ja Duplikate gezogen werden, deswegen benutzt man kein set, sondern einen vector für die gezogenen Zahlen?
    Ja, ich verstehe dein Argument, aber in meinen Augen ist das hier völlig überzogen.



  • @PadMad
    Generischen Code schreibt man gegen Interfaces, nicht gegen konkrete Datentypen. Ein gutes Beispiel sind da die STL-Algorithmen, die meistens Iteratoren erwarten und nur Anforderungen an den Iterator-Typen und den benutzten Datentypen stellen. Als Beispiel mal die std::sort Funktion mit zwei Parametern: Sie erwartet zwei Random-Access-Iteratoren und dass der Datentyp, den die Iteratoren referenzieren, per < vergleichbar sein müssen. Woher die Iteratoren kommen (vector, list, deque, sonstwas) ist der Funktion egal. Wie der < Operator umgesetzt wird ist fast egal, Hauptsache er existiert.



  • @DocShoe Das stimmt - die Interface Ebene ist für mich Teil der Datentyp-Ebene - aber wäre das dann nicht tatsächlich eine Performance-Einbuße? Gerade bei std::sort? Aus dem Bauch heraus würde ich vermuten, dass unterschiedliche Datenstrukturen aus STL unterschiedlich performant sind, wenn es um's Sortieren geht.

    Das kann ja auch valide sein, wenn in diesem Moment die Generik wichtiger ist als die Performance - aber ich kann mir gerade nicht vorstellen, dass Generik dann nicht auch mit Kosten kommt.

    (btw. das sind tatsächlich nur Gedanken - wenn ich hierbei etwas lerne, würde ich mich tatsächlich freuen)



  • @PadMad sagte in Array übergeben zu Konstruktor:

    @Belli Was meinst Du damit? Wenn ich einer Datenstruktur ein Element hinzufüge, das aber eigentlich nicht soll / darf, weil ich nicht möchte, dass meine Datenstruktur weiter wächst, wenn eine bestimmte Größe erreicht ist, muss ich das so oder so im Code abfangen.

    Ich sehe keinen Vorteil von

    array<string, 7> weekdays = {"Mo", "Di", /*usw*/};
    

    gegenüber

    const vector<string> weekdays = {"Mo", "Di", /*usw*/};
    

    Das Array sollte übrigens vermutlich auch const sein?!

    @PadMad sagte in Array übergeben zu Konstruktor:

    Weil letzten Endes hat @Johnny01 gefragt wie man ein "array" an einen Konstruktor übergeben kann.

    Ja, das stimmt, aber er hat nicht ein std::array gemeint, sondern ein c-array.



  • Ja, das stimmt, aber er hat nicht ein std::array gemeint, sondern ein c-array.

    Korrekt. Aber was möchtest Du mir jetzt damit sagen? Dass, wenn Jemand von einem c-array redet, man durchaus den Vorschlag machen darf, dass std::vector eine gute Alternative ist, std::array aber nicht? std::vector ist jetzt erstmal auch kein c-array...

    Das Array sollte übrigens vermutlich auch const sein?!

    Natürlich - ist es ja auch - spätestens bei der Übergabe in meinem Beispiel - zudem ist const in meinem Beispiel ja völlig egal, weil ich auf die Größe der Datenstruktur angespielt habe.
    Und ich persönlich sehe hier optisch durchaus einen Unterschied, was die Anzahl der Elemente betrifft... vielleicht habe ich mich aber auch verzählt...

    std::array<std::string, 7> _weekDays = {"Mo","Di","Mi","Do","Fr","Sa","So"};
    std::vector<std::string> _weekDaysAndMore = {"Mo","Di","Mi","Do","Fr","Sa","So","1","42","0xfff","*g*"};
    

    Wenn man dieses Beispiel stellvertretend für Daten nimmt, die von Jemandem kommen, der eine API nutzen möchte mit diesem besagten Endpunkt - wo die Anzahl der Elemente fix sein soll, in diesem Fall 7 - warum sollte ich std::vector nutzen?
    Ob const oder nicht - völlig egal... weil, ob ich meine falschen Eingaben nun const mache oder nicht, macht ja die Eingaben nicht weniger falsch...

    Deswegen sollte meiner Meinung nach der besagte API Endpunkt std::vector gar nicht erst akzeptieren.


  • Mod

    @DocShoe sagte in Array übergeben zu Konstruktor:

    @SeppJ

    Klar fällt mir was auf. Sind deine Datenstrukturen alle als Templates definiert, wo du den Containertyp als Template Parameter festlegst? Bei irgendeiner bekloppten Lotterie könnten ja Duplikate gezogen werden, deswegen benutzt man kein set, sondern einen vector für die gezogenen Zahlen?

    ??? Warum sollten die internen Strukturen Templates sein?

    Ja, ich verstehe dein Argument, aber in meinen Augen ist das hier völlig überzogen.

    Da du offensichtlich genau so ein Fall wie in meinem Beispiel bist, ist das wohl um so mehr ein Argument dafür, das auch am allerkleinsten Popelbeispiel konsequent durchzuziehen. Wenn man's nie richtig gelernt hat, ist ja klar, dass es nie wie versprochen funktioniert und man das als overengineerten, unnötigen Aufwand ohne Mehrwert abtut. Es ist schließlich overengineerter, unnötiger Aufwand ohne Mehrwert, wenn man nur die Bewegungen nachäfft.

    Ist genauso wie bei objektorientierter Programmierung, wo das eigentlich eine ganz andere Denkweise ist, aber viele Leute schreiben vor schlechten Code ein class davor und denken dann "Jetzt ist es objektorientiert!". Heimlich denken sie sich, was der ganze Informatikerquatsch eigentlich soll, denn der Code ist immer noch genauso schlecht wie vorher. Und klopfen sich dann auch selber auf die Schulter wenn sie erklären, dass nicht alles eine Klasse sein braucht und sie über solchen weltfremden Paradigmen stehen. (Dabei merken sie nicht einmal, dass es nicht um Klassen sonder um eine Denkweise geht).



  • @SeppJ sagte in Array übergeben zu Konstruktor:

    Ihr beide klingt so, als wäre generischer Code für euch kein riesengroßer Vorteil, der jedwede andere Überlegung hinfällig macht.

    Du klingst so, als ob man ohne generischen Code nicht anfangen könnte, Programmieren zu lernen.

    Für mich:

    1. vector lernen
    2. andere Containerklassen erlernen
    3. zeigen, dass man bei allen Containern mit Iteratoren loopen kann
    4. lernen Templates zu schreiben
    5. Funktionen mit Iteratoren erstellen

    Für dich (überspitzt):

    1. generischen Code lernen
    2. vector einführen

    Es ist nicht so, dass ich nie die 2-Iterator-Lösung verwende. Aber den Kontext, in dem eine Frage gestellt wurde, darf man auch nicht ignorieren. Templates (und dir brauchst du, eine Funktion mit zwei std::vector<int>::const_iterator-Paramtern ist ja recht sinnfrei) sind nun mal nicht einfach. Klar muss man die irgendwann benutzen können, wenn man C++ richtig lernen will.

    Es hängt übrigens auch sehr stark vom Umfeld ab, in dem du arbeitest. Wenn das alles Informatiker sind, ist es was anderes als wenn es Leute aus anderen Bereichen sind, die aber programmieren müssen. Und da zählt das Argument "einfacher verständlich" mehr als "generisch". (das ist auch ein wesentlicher Grund, warum ich inzwischen zu 90% Python schreibe).



  • @SeppJ sagte in Array übergeben zu Konstruktor:
    ...

    ??? Warum sollten die internen Strukturen Templates sein?
    ...

    Schon gut...


  • Mod

    @wob sagte in Array übergeben zu Konstruktor:

    @SeppJ sagte in Array übergeben zu Konstruktor:

    Ihr beide klingt so, als wäre generischer Code für euch kein riesengroßer Vorteil, der jedwede andere Überlegung hinfällig macht.

    Du klingst so, als ob man ohne generischen Code nicht anfangen könnte, Programmieren zu lernen.

    Nee, ist schon Ok, das man das nicht als erstes lernt. Aber wenn man's einem Anfänger vormacht, dann bitte nicht falsch, oder wenigstens mit einem großen Exclaimer dass das nur zu Lernzwecken dient. Code, der in C++ eine explizite Containervariante vorschreibt um eine Menge von Daten zu übergeben, ist einfach falsch. Das muss so auch gesagt werden und jedem bewusst sein.

    Es hängt übrigens auch sehr stark vom Umfeld ab, in dem du arbeitest. Wenn das alles Informatiker sind, ist es was anderes als wenn es Leute aus anderen Bereichen sind, die aber programmieren müssen. Und da zählt das Argument "einfacher verständlich" mehr als "generisch". (das ist auch ein wesentlicher Grund, warum ich inzwischen zu 90% Python schreibe).

    Aber gerade in Python wird doch überall Generik praktiziert und es eine der großen Stärken der Sprache, dass es dort so einfach ist! Man muss ja nicht einmal darüber nachdenken, es ist der Default, dass alles komplett generisch ist. Um so schlimmer (und ein bekanntes Antipattern!) ist in Python Code, der irgendwelche Typprüfungen macht. Jeder Best-Practices-Guide für Python sagt, möglichst niemals Konstrukte wie if isinstance(variable, list) zu nutzen. Stell dir mal vor, sum würde nur mit list funktionieren. Würde niemals jemand so schreiben!



  • Du meinst falsch im gleichen Sinne wie falsch, wenn eine Funktion einen std::vector zurückgibt, anstatt einen Output Iterator zu erwarten, der beschrieben wird?
    Sorry, das ist lächerlich. Ne, nichtmal sorry, das ist einfach nur lächerlich.


  • Mod

    @DocShoe sagte in Array übergeben zu Konstruktor:

    Du meinst falsch im gleichen Sinne wie falsch, wenn eine Funktion einen std::vector zurückgibt, anstatt einen Output Iterator zu erwarten, der beschrieben wird.
    Sorry, das ist lächerlich.

    Ja, dann ist die STL eben lächerlich. Alles lächerliche Theoretiker. Verstanden.



  • Die STL ist eine wiederverwendbare Bibliothek, eine Filmdatenbank ist ein konkretes Projekt mit konkreten Datentypen.


  • Mod

    @DocShoe sagte in Array übergeben zu Konstruktor:

    Die STL ist eine wiederverwendbare Bibliothek, eine Filmdatenbank ist ein konkretes Projekt mit konkreten Datentypen.

    Wie ich sagte, du hast nicht einmal verstanden, was dein Problem ist, obwohl es dir erklärt wurde.

    @SeppJ sagte in Array übergeben zu Konstruktor:

    @SeppJ sagte in Array übergeben zu Konstruktor:

    Man denkt sich zwar immer "Ich habe von generischer Software gehört, aber ich werde niemals einen zweiten Karumbulflator schreiben oder benutzen, wieso sollte ich das generisch machen?" […] Wahrscheinlich merkt man noch nicht einmal, dass da eigentlich Wiederbenutzbarkeitspotential war, weil man es nicht gewöhnt ist, wiederbenutzbar zu programmieren […] Und dann klopft man sich auf die Schulter, dass man nicht auf diesen weltfremden Informatikerquatsch aus deren Elfenbeiturm angewiesen ist, den man ja in der Praxis nie braucht.

    Fällt dir was auf?



  • Lassen wir das, das führt zu nichts.


  • Mod

    @DocShoe sagte in Array übergeben zu Konstruktor:

    Lassen wir das, das führt zu nichts.

    Bei dir nicht, aber andere können vielleicht was lernen.

    @Andere: Was DocShoe hier beispielsweise nicht sieht, ist, dass seine ach so konkrete Filmdatenbank mit ihren ach so konkreten Typen, eine Spezialisierung eines Datenbankkonzepts ist. Man könnte hier eine generische Datenbank haben und dann als Oberfläche eine Zeile Code, die aus der Datenbank eine Filmdatenbank macht. Aber das ist weltfremder Informatikerquatsch. Niemand auf der Welt hätte jemals Anwendung für eine generische Datenbank. Würde in der Ecke verstauben und niemals wieder benutzt werden. Jede Datenbank wird immer speziell für jeden Anwendungsfall neu entwickelt. Von Profipraxisprogrammierern, die sich nicht von theoretischem Firlefanz beeindrucken lassen.



  • @SeppJ Ich muss mich ja doch etwas über den unprofessionellen Ton wundern, den Du hier an den Tag legst - gerade mit dem Moderator-Hintergrund, den Du zu haben scheinst.

    Solch Diskussionen sollten konstruktiver Natur sein - dabei darf man auch anderer Meinung sein - aber findest Du nicht, dass deine Formulierungen teilweise etwas zu weit gehen?

    Btw.: Das Thema an sich wurde auch ganz schön aufgebläht - die ursprüngliche Frage war recht einfach - mittlerweile gibt es dazu so viele Gedankengänge in diesem Thread - teilweise auch außerhalb des professionellen Rahmens - dass es anfängt peinlich zu werden.
    Gerade als Moderator sollte man versuchen eine Diskussion in professionelle Bahnen zu lenken.



  • Irgendwie kommen wir vom Thema ab.

    Die Ursrpüngliche Aussage war, dass man nie Container als Übergabe Paramter verwenden soll. Losgelöst von der Generik.

    Möglich wäre ja auch sowas, was durchaus generisch wieder verwendbar wäre, aber nach der Aussage auch abzulehnen sei:

    template<typename Range>
    void sort(Range&& r)
    {
      std::sort(std::begin(r), std::end(r));
    }
    

    (ganz locker an Boost.range angelehnt). Oder meinst du @SeppJ das mit

    oder meinetwegen auch ein Templateparameter aus dem der Code sich dann selber eine Iteration macht

    Aber, ich glaube auch, dass es sinnvolle Anwendungen für spezialisierte Container Übergaben gibt. QT nutzt das z.B. auch als Möglichkeit um Items an eine ListView zu übergeben.



  • @Schlangenmensch sagte in Array übergeben zu Konstruktor:

    Aber, wenn ich wirklich Daten einer unbekannten Anzahl weiter geben will / muss, was spricht dagegen die in einem Vektor weiter zu geben?

    Eigentlich ist das unnötig, da man das Design so wählen kann, dass das nicht notwendig ist. Denn woher sollen die flexiblen Mengen an Parametern kommen? IO? Wenn IO dann kann man gleich das Objekt korrekt befüllen.

    Bei std::initializer_list muss die Länge der Liste ja auch zur Compilezeit feststehen, ist also, wenn die Länge erst zur Laufzeit feststeht, nicht geeignet.

    Dafür kann man die Objekte direkt mit Listen von Parametern befüllen.

    Days Weekdays = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"};
    

  • Mod

    @Schlangenmensch sagte in Array übergeben zu Konstruktor:

    Möglich wäre ja auch sowas, was durchaus generisch wieder verwendbar wäre, aber nach der Aussage auch abzulehnen sei:

    template<typename Range>
    void sort(Range&& r)
    {
      std::sort(std::begin(r), std::end(r));
    }
    

    (ganz locker an Boost.range angelehnt). Oder meinst du @SeppJ das mit

    oder meinetwegen auch ein Templateparameter aus dem der Code sich dann selber eine Iteration macht

    So sollte man das machen, und so ist das von mir gemeint. Oder halt meinetwegen mit Iteratoren, wenn man auf älteren Sprachstandards unterwegs ist. Diese beiden Möglichkeiten muss jeder wissen, dass das die Methode ist, wie man containerartige Datenmengen herumreicht. Ausnahmen gibt es nur, wenn man selber containerartige Strukturen schreibt, da ist klar, dass die natürlich auch sich selber benutzen können. Und std::string, mathematische Vektoren und ähnliches, weil die eine spezielle Semantik haben, die über "Folge von Zeichen/Zahlen" hinaus geht.



  • @SeppJ sagte in Array übergeben zu Konstruktor:

    Code, der in C++ eine explizite Containervariante vorschreibt um eine Menge von Daten zu übergeben, ist einfach falsch. Das muss so auch gesagt werden und jedem bewusst sein.

    Das ist einfach nicht wahr.

    Der Code ist vielleicht nicht idiomatisch. Aber falsch noch lange nicht.

    Außerdem: man hat ja oftmals auch Code, der unterschiedliche Performacecharakteristika hat je nach Container bzw. bei dem für jeden Container ein anderer Algorithmus besser ist - d.h. ich muss eh wissen, ob ich da eine Liste oder einen Vector habe. (Gut, du wirst jetzt sagen, ich muss wissen, ob ich einen RadomAccessIterator oder einen ForwardIterator habe) Alles schön und gut. Aber mit einem konkreten Objekt arbeitet es sich nun einfach mal leichter. Und nicht jeder Code muss generisch sein, finde ich.

    Um so schlimmer (und ein bekanntes Antipattern!) ist in Python Code, der irgendwelche Typprüfungen macht. Jeder Best-Practices-Guide für Python sagt, möglichst niemals Konstrukte wie if isinstance(variable, list) zu nutzen.

    Und auch hier muss ich widersprechen. Ich habe solchen Code nämlich. Der geht eine Datenstruktur beim Serialisieren rekursiv durch - und der testet sehr wohl direkt auf "list" (und noch ein paar andere Typen, die alle gesondert behandelt werden). Und ich habe auch diverse Funktionen, die 1 Arg oder einen Container zulassen - auch da muss ich explizit checken - und zwar auf "not isinstance(obj, str)", weil Strings nämlich auch iterable sind (und ich eben den Fall 1 String oder mehrere Strings zulassen will)