große Vektoren überladen



  • Hey Leute,
    ich bin gerade dabei C++ zu lernen, und befasse mich aktuell mit der Überladung von Operatoren.
    Das Grundprinzip habe ich, denke ich mal, verstanden. Meine Frage ist also etwas spezifischer Natur.

    In meinem Szenario habe ich einen Vektor, mit mehreren tausend Werten. Jetzt möchte ich mit Überladung, jedem
    dieser Werte einen Integer addieren, und zwar immer den gleichen. Ich habe in mehreren Codebeispielen gesehen,
    dass ich händisch angeben muss, was addiert werden soll, sprich jeden Wert im Vektor aufzählen soll. Bei tausenden Werten natürlich sehr umständlich.
    Habt ihr eine Idee, den Ablauf zu automatisieren? Ich hoffe, ich konnte mein Problem verständlich ausdrücken.
    Beste Grüße,
    Calumeth





  • Pre C++17: std::transform

    std::vector<int> values;
    auto increment_one = []( int& val )
    {
       return val +1;
    };
    std::transform( values.begin(), values.end(), values.begin(), increment_one );
    


  • Was spricht gegen eine einfache for-Loop?

    std::vector<int> werte;
    (...)
    for (auto &wert : werte) wert += 2;  // statt 2 kannst du hier irgendeinen Wert nehmen
    

    Und ja, ich finde das einfacher & deswegen besser als hierfür einen std-Algorithmus zu nehmen.

    Edit: Mir ist noch nicht ganz klar, was das mit "Operatorüberladung" zu tun hat. Gut, für int gibt es +. Aber ist das wirklich das, worauf @Calumeth hinauswill?



  • @wob
    Sean Parent spricht dagegen 😉

    Bei trivialen Sachen nehme ich aber auch meistens die for-Schleife, transform ist iwie unhandlich.


  • Mod

    Überladen von Operatoren für Standardtypen ist ein tolles Rezept für unerwartete Probleme. Besonders wenn man noch nicht einmal selber Schleifen programmieren kann…

    Was hat die Frage überhaupt mit Überladung zu tun?



  • @DocShoe Ich kenne den Talk und glaube nicht, dass er dagegen ist. Gegenfrage: kennst du die Slide 39 aus dem Talk?


  • Mod

    Slide 4 sagt doch schon direkt am Anfang "A raw loop is any loop inside a function where the function serves purpose larger than the algorithm implemented by the loop". Wenn die Funktionalität ist "addiere zu jedem Element einen Wert", dann gibt es keinen larger purpose jenseits der Schleife. Ich bin hier ganz bei wob.



  • Nur der Vollständigkeit halber sei noch std::valarray erwähnt:

    #include <valarray>
    int main()
    {
      std::valarray<int> v={0, 1, 2, 3};
    
      v+=1; // alle Elemente um 1 erhöhen
    }
    

    Ob es passt, weiß ich natürlich nicht (ich verwende einen std::vector auch lieber).



  • @wob sagte in große Vektoren überladen:

    Und ja, ich finde das einfacher & deswegen besser als hierfür einen std-Algorithmus zu nehmen.

    Ein paar Überlegungen dazu (allgemeiner Art, nicht auschliesslich auf transform bezogen):

    Man könnte argumentieren dass die Implementierung von transform-ähnlichen Algorithmen mehr Freiheiten hat, die Gesamtoperation auf verscheidene Art und Weise durchzuführen, wodurch sich mehr Optimierungspotential ergibt - wenn denn eine Bibliothek den Aufwand betreiben will - wenn nicht, dann ist es trotzdem mindestens so gut wie der handgeschriebene Loop.

    Das liegt vor allem daran, dass ein ganzer Bereich statt nur einzelne Elemente übergeben werden. Theoretisch könnte so ein transform-ähnlicher Argorithmus z.B. den Bereich der Elemente partitionieren und die Opratationen parallel ausführen. Oder z.B. interne SIMD-Optimierungen durchführen, wenn z.B. die Art der Operation wie increment_one bekannt ist, die Addition für meherere Elemente gleichzeitig mittels Vektorinstruktionen der CPU durchführen.

    Auch muss z.B. der an increment_one übergebene int& theoretisch nicht einmal das tatsächliche Range-Element referenzieren, sondern es kann z.B. auch ein temporäres oder gecachtes Objekt sein, das aus irgendeinem Grund in diesem Kontext Sinn macht.

    Bei der for-Schleife gibt es diese Optimierungsmöglichkeiten für die Bibliotheks-Entwickler nicht. Da kann das höchstens der Compiler machen.



  • @Finnegan sagte in große Vektoren überladen:

    Theoretisch könnte so ein transform-ähnlicher Argorithmus z.B. den Bereich der Elemente partitionieren und die Opratationen parallel ausführen.

    Der Standard definiert bereits eine (potentiell) parallele transform-Version.



  • Also erstmal vielen Dank für die schnellen und vielen Antworten. Ich versuche gleich mal die Vorschläge in die Tat umzusetzen.

    Hier einfach mal die Aufgabenstellung. Das soll kein Aufruf zu einer Lösung sein, sondern einfach das Problem veranschaulichen:

    Eine Klasse Signal, die eine Folge von Messwerten repräsentiert. Die Anzahlder Messwerte sei daher zunächst unbekannt. Signale müssen sich aus einspaltigen Textdateien einlesen
    lassen, die Dezimalzahlen enthalten. Signale müssen sich in einspaltige Textdateien speichern lassen. In beiden Fällen muss der Dateiname frei festlegbarsein. Die Signalklasse muss Methoden enthalten, um Anzahl, Minimum, Maximum, Summe, Mittelwert und Standardabweichung der Messwerte zu berechnen. Es muss möglich sein, Signalen einen konstanten Dezimalwert zu addieren und zu subtrahieren sowie sie mit einem konstanten Dezimalwert zu multiplizieren. Geeignete Operatoren sollten hierfür überladen sein. Es muss möglich sein, das Signal durch einen Moving-Average-Filter wählbarer Breite zu glätten.
    (Zitat aus der Aufgabenstellung)

    Meint ihr, ich habe das Problem richtig verstanden, dass ich auf jede Zahl im Vektor mit HIlfe eines überladenen Operanden jeweils einen int wert addieren/subtrahieren/multiplizieren können soll?

    Also sprich "vektor a + 10"?

    Cheers!



  • @wob sagte in große Vektoren überladen:

    Was spricht gegen eine einfache for-Loop?
    Und ja, ich finde das einfacher & deswegen besser als hierfür einen std-Algorithmus zu nehmen.

    Seh ich genauso. Und wenn man unbedingt einen std::algrithm nehmen will, dann reicht hier ein Lambda. Da muss man nicht noch was außerhalb der Funktion definieren.


  • Mod

    @Calumeth sagte in große Vektoren überladen:

    Meint ihr, ich habe das Problem richtig verstanden, dass ich auf jede Zahl im Vektor mit HIlfe eines überladenen Operanden jeweils einen int wert addieren/subtrahieren/multiplizieren können soll?

    Also sprich "vektor a + 10"?

    Nicht ganz. Gemeint ist objekt_von_deiner_signalklasse + 1.



  • Ah okay! Wie bei Java damals. Ich instanziiere also von außerhalb ein Objekt meiner Signalklasse, und soll dann mit den Rechenoperatoren +,-,* damit rechnen können. In diesem Abbild meiner Klasse sind auch meine eingelesenen Werte vorhanden (im Vektor gespeichert), auf die sich die Rechnungen auswirken sollen.
    So macht es in meiner Welt gerade Sinn, ....oder?



  • ja.



  • Okay, super! ich versuche jetzt mal alles zu ordnen und auch schon zu programmieren. So ganz hatte ich Überladung wohl doch noch nicht verstanden. Ich melde mich nochmal bei, wenn ich einen Schritt weitergekommen bin.
    Vielen, vielen Dank schonmal für eure Hilfe!!



  • @Calumeth sagte in große Vektoren überladen:

    Wie bei Java damals. Ich instanziiere also von außerhalb ein Objekt meiner Signalklasse, und soll dann mit den Rechenoperatoren +,-,* damit rechnen können.

    Also wenn Java Operator Overloading unterstützt, dann hab ich da gewaltig 'was verpasst.
    (Nicht dass ich Java verwenden würde, aber so gundlegende Dinge interessieren mich dann schon, auch bei Sprachen die ich verabscheue.)



  • Na komm, ob nun object.add(other); oder object += other; macht doch keinen so großen Unterschied.

    Nachdem ich mal 9 Monate Java gemacht hatte (länger hab ich Webshops & Co nicht ausgehalten), fehlten mir in C++ zwei Dinge:

    • enum classes. In Java kann man enums mit Objekten haben, die dann z.B. bestimmte getter haben. Das ist sehr sehr praktisch. In C++ müsste man dann x static Objekte einer Klasse anlegen und irgendwie zusehen, dass der Konstruktor nicht öffentlich ist. Sehr unschön.
    • In Java läuft der Constructor erst, wenn das Objekt seinen finalen Typ hat, d.h. ich kann dort auch bereits virtuelle Funktionen aufrufen. In C++ geht das so nicht, weil der Constructor dafür "zu früh" läuft. Vielleicht war das Pattern nicht optimal, im Constructor bereits virtuelle Funktionen aufzurufen, aber das fehlte mir in C++ eine Zeit lang deutlich.
    • generell hatte ich in Java das Gefühl, dass wir ständig hunderte Klassen geschrieben haben, die eigentlich nur irgendwas durchgereicht haben und man beim Debuggen von einer Klasse zur nächsten gekommen ist, ohne dass eigentlich irgendwas passiert wäre.
    • Java kompiliert "etwas" schneller als C++

    Das ist sicher sehr subjektiv und es gibt viel mehr. Aber das sind die Punkte, an die ich mich am meisten erinnere.



  • @wob sagte in große Vektoren überladen:

    Na komm, ob nun object.add(other); oder object += other; macht doch keinen so großen Unterschied.

    Also für mich macht es schon einen Unterschied ob Strings mittels

    str1 == str2
    

    oder

    str1.equals(str2)
    

    verglichen werden. Compiliert nämlich leider beides, nur das erste vergleicht eben nur den Inhalt der Referenzen und nicht der Objekte selbst. Gerade fehlendes operator Overloading lässt mich Java ziemlich hassen (nicht nur das, ist aber auch ein riesen Punkt).

    Werde ja gerade durch die Uni dazu gezwungen mir Java anzueignen. Das ist mit einem Background von knapp 15 Jahren c++ jetzt kein Hexenwerk aber bringt einen auch teilweise echt zum Verzweifeln^^

    Dazu gesellen sich dann noch solche Dinge wie fehlender impliziter copy-support, DTors, const-correctness, generelle value semantics von Objekten, etc.


Log in to reply