Mehrere Vektoren in neuem Vektor zusammenfügen



  • Hallo Forengemeinde,

    Ich habe also mehrere stl::vector in denen Pointer auf eigene Klassen gespeichert sind. Deren Inhalt soll nochmal an zentraler Stelle in einem anderen Vektor zusammen gespeichter werden.
    Mein Ziel ist es entweder sowas hier schreiben zu können:

    SammelVektor = Vektor1 + Vektor2 + ...
    

    Dafür habe ich jetzt dieses Operator-Template definiert:

    template <class T> std::vector<T> operator +(std::vector<T> Vektor1, std::vector<T> Vektor2){
    					std::vector<T> SammelVektor;
    					SammelVektor.insert(SammelVektor.end(),Vektor1.begin(), Vektor1.end());
    					SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
    					return SammelVektor;
    				}
    

    Das Funktioniert zumindest mit vector<int> wunderbar. Mit Vektoren die integer enthalten funktioniert allerdings auch der "=" Operator, für meine Vektoren scheint aber kein geeigneter "=" Operator zu existieren, wie ich leider feststellen musste nachdem ich mit + fertig war.
    Deswegen die Fragen:
    Muss ich den "=" Operator auch noch selbst schreiben? In der Beschreibung zu Vektor steht ja das er ein Template für "=" hat. Weiso tuts "=" mit vector<int>, aber nicht mit meinen Vektoren?

    Die zweite Idee war: 
    Sammelvektor+=Vektor1;
    Sammelvektor+=Vektor2;
    ...
    

    Mit nem überladenen Opeator "+=".
    Aber dabei wurde Sammelvektor am Ende der Funktion immer erased und ich wusste nicht wie ich dafür sorgen kann, dass die Einträge dauerhaft in Sammelvektor
    gespeichert werden.
    Frage hierzu: Wie kann man soetwas mit nem überladenen Operator realisieren?

    Ich freue mich auf eure Tips. 😃



  • Ich habe nicht so ganz genau verstanden, was dein Problem ist, wenn ich dich richtig verstanden habe geht:

    vector<int> v1, v2;
    vector<int> sv = v1 + v2;
    

    aber

    vector<MyClass*> v1, v2;
    vector<MyClass*> sv = v1 + v2;
    

    nicht?

    Zur Frage der Operatorüberladung: Du kannst den Zuweisungsoperator nicht überladen, da er als Member definiert werden muss. Ich sehe allerdings auch keinen Grund dafür, es tun zu wollen, weil der normale Zuweisungsoperator vollkommen ausreicht.
    Ist es gewollt, dass du die Paramter per value übergibst? Gerade bei std::vector sind Kopien ziemlich teuer und es spricht nichts dagegen, sie hier als const Reference zu übergeben.



  • medic123123 schrieb:

    Das Funktioniert zumindest mit vector<int> wunderbar. Mit Vektoren die integer enthalten funktioniert allerdings auch der "=" Operator, für meine Vektoren scheint aber kein geeigneter "=" Operator zu existieren, wie ich leider feststellen musste nachdem ich mit + fertig war.

    Es sollte genau so funktionieren, poste doch mal die genau Fehlermeldung.
    Haben alle Pointer den selben Typ?

    Verbesserungsvorschlag für den operator+:

    template <class T> std::vector<T> operator +( const std::vector<T>& Vektor1, const std::vector<T>& Vektor2) {
        std::vector<T> SammelVektor( Vektor1 );
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    .. sollte etwas performanter sein.

    medic123123 schrieb:

    Die zweite Idee war:
    Sammelvektor+=Vektor1;
    Sammelvektor+=Vektor2;
    ...
    [/code]
    Mit nem überladenen Operator "+=".
    Aber dabei wurde Sammelvektor am Ende der Funktion immer erased ...

    Den +=operator kannst Du nicht selber schreiben, da er innerhalb der jeweiligen Klasse - in diesem Fall std::vector<> - als Member definiert sein muss.



  • Ein insert ist viel aussagekräftiger als das was du machen willst, denn die Semantik von + auf vector s ist nicht von vornherein klar. Ich würde mir das also sparen.
    Außerdem implementiert man den operator+ üblicherweise durch eine operator+= -Memberfunktion. Dass das hier nicht geht, ist ein Hinweis darauf, dass es keine gute Idee ist operator+ zu überladen.
    Und drittens: Du hast doch die Zeiger schon in deinen einzelnen vector s, wieso willst du noch einen "Sammelvektor"?



  • Vielen Dank soweit. Die schnellere variante kann ich gut gebrauchen.

    @ntrnt: Es ist auch aus Gründen der Geschwindigkeit, dass ich die Vektorinhalte nochmal sammel. Denn anschließend soll auf die Objekte in den Vektoren nen Algorhitmus losgelassen werden der Berechnungen durchführt Daten aus den Objekten ausliest und wieder reinschreibt und das viele viele Male. Und die einzelnen Vektoren liegen ziemlich verstreut in tausenden (verschiedenen)Instanzen vor, weswegen es vielleicht zeitlich günstiger ist sie nur einmal zu sammeln bevor immerwieder die selbe Berechnung durchgeführt wird.
    In den einzelnen Instanzen werden diese Objekte allerdings auch für verschiedene Dinge benötigt. Deswegen diese "komische" Auuftrennung und Sammlung.

    @all:
    Fehlermeldung: Intellisense: Kein "=" Operator stimmt mit diesen Operanden überein.
    Kompilieren: Binärer Operator '=': Es konnte kein Operator gefunden werden, der einen linksseitigen Operanden vom Typ 'const std::vector<_Ty>' akzeptiert (oder keine geeignete Konvertierung möglich)
    1> with
    1> [
    1> _Ty=BattPackage::MyClass *
    1> ] ...



  • Wenn du jetzt noch den entsprechenden Code (oder den einer reduzierten Variante, die das Verhalten das du beschreibst hervorruft) zeigst, kann man dir vielleicht sogar helfen.
    Tipp: Irgendwas ist konstant, was nicht konstant sein sollte.

    Wenn du wirklich zur Datenduplizierung gezwungen bist, dann deutet das i.A. auf Redundanz im Design hin.



  • Was sagt denn Dein Profiler, wo die Zeit "vertrödelt" wird? Den würde ich ja als erstes anschmeißen, um zu gucken, was man vielleicht umbauen sollte. Beispielsweise kann ich mir vorstellen, dass Dein Ansatz gar nicht viel helfen wird, weil Du am Ende immer noch einen Vektor hast, der nur Zeiger speichert und die Objekte, auf die gezeigt werden irgendwie in beliebiger Reihenfolge im Speicher stehen können. Damit produzierst Du so oder so relativ viele "cache misses".

    Werner_logoff schrieb:

    Verbesserungsvorschlag für den operator+:

    template <class T>
    std::vector<T> operator +( const std::vector<T>& Vektor1, const std::vector<T>& Vektor2)
    {
        std::vector<T> SammelVektor( Vektor1 ); // <-- teuer
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    ... sollte etwas performanter sein.

    Verbesserungsvorschlag für den Verbesserungsvorschlag:

    template <class T>
    std::vector<T> operator +( std::vector<T> Vektor1, const std::vector<T>& Vektor2)
    { // Kopie kann ggf wegoptimiert werden ^^^^^^^^^
        std::vector<T> SammelVektor; // <-- billig
        SammelVektor.swap(Vektor1);  // <-- billig
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    Wenn Du durch Einsatz von pass-by-value eine manuelle Kopie sparen kannst, bevorzuge pass-by-value und lass den Compiler etwas kopieren, wenn es nötig ist. Diese Implementierung ist in keinem Fall schlechter als Deine, nutzt aber die "copy elision"-Optimierung bei modernen Compilern aus.



  • Warum dann noch den Umweg über SammelVektor?

    template<typename T>
    std::vector<T> operator +( std::vector<T> v1, const std::vector<T>& v2 )
    {
       v1.insert( v1.end(), v2.begin(), v2.end() );
       return v1;
    }
    


  • DocShoe schrieb:

    Warum dann noch den Umweg über SammelVektor?

    Er hat ja gesagt, v1 und v2 müssen dann noch unanbhängig voneinander auf unterschiedliche Weise bearbeitet werden.



  • medic123123 schrieb:

    @all:
    Fehlermeldung: Intellisense: Kein "=" Operator stimmt mit diesen Operanden überein.
    Kompilieren: Binärer Operator '=': Es konnte kein Operator gefunden werden, der einen linksseitigen Operanden vom Typ 'const std::vector<_Ty>' akzeptiert (oder keine geeignete Konvertierung möglich)
    1> with
    1> [
    1> _Ty=BattPackage::MyClass *
    1> ] ...

    Dein Compiler sagt, dass der Vektor, dem Du das Ergebnis Deiner Berechnung zuweisen willst, const ist. Hat mit dem operator+ nichts zu tun.



  • krümelkacker schrieb:

    Verbesserungsvorschlag für den Verbesserungsvorschlag:

    template <class T>
    std::vector<T> operator +( std::vector<T> Vektor1, const std::vector<T>& Vektor2)
    { // Kopie kann ggf wegoptimiert werden ^^^^^^^^^
        std::vector<T> SammelVektor; // <-- billig
        SammelVektor.swap(Vektor1);  // <-- billig
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    Wenn Du durch Einsatz von pass-by-value eine manuelle Kopie sparen kannst, bevorzuge pass-by-value und lass den Compiler etwas kopieren, wenn es nötig ist. Diese Implementierung ist in keinem Fall schlechter als Deine, nutzt aber die "copy elision"-Optimierung bei modernen Compilern aus.

    Ok - man lernt ja nie aus. Und was ist der Unterschied obiger Variante zu

    template <class T>
    std::vector<T> operator +( std::vector<T> SammelVektor, const std::vector<T>& Vektor2)
    {
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    ? - da war doch was, dass dann RVO nicht mehr gehen soll. Habe ich mal gehört/gelesen.



  • 1.@all:Code:

    using namespace std;
    #include "Cells.h"
    
    void BattPackage::Cells::vCreateNetwork() const {
    ...
    this->vecAllGNodes = this->vecCellArea[0]->ptRGrid->vecGNode
    ...
    }
    

    Fehlermeldung: Intellisense: Kein "=" Operator stimmt mit diesen Operanden überein.
    Kompilieren: Binärer Operator '=': Es konnte kein Operator gefunden werden, der einen linksseitigen Operanden vom Typ 'const std::vector<_Ty>' akzeptiert (oder keine geeignete Konvertierung möglich)
    1> with
    1> [
    1> _Ty=BattPackage::MyClass *
    1> ] ...

    2. Mein Profiler sagt gar nichts, da ich keinen habe. Ich werd mich darüber mal schlau machen...

    3.Ja der "+" Operator scheint zu funktionieren. Aber ohne den "=" Operator für meine Vektoren anwenden zu können bringt mir das nicht viel. Aus welchem (wahrscheinlich ziemlich trivialen Grund) will der denn nicht so wie er soll?
    Beide Operanden sind nicht explizit als const deklariert...



  • medic123123 schrieb:

    1.@all:Code:

    using namespace std;
    #include "Cells.h"
    
    void BattPackage::Cells::vCreateNetwork() [b]const[/b] { // <=================
    ...
    this->vecAllGNodes = this->vecCellArea[0]->ptRGrid->vecGNode
    ...
    }
    


  • Argh...gar nicht mehr drauf geachtet...
    Jetzt tuts wie es soll.
    Vielen vielen dank soweit. Ich werde mal versuchen den ganzen input, den ich hier gekriegt habe zu verarbeiten.

    Gruß, Michael



  • Werner_logoff schrieb:

    krümelkacker schrieb:

    Verbesserungsvorschlag für den Verbesserungsvorschlag:
    [...]
    Wenn Du durch Einsatz von pass-by-value eine manuelle Kopie sparen kannst, bevorzuge pass-by-value und lass den Compiler etwas kopieren, wenn es nötig ist. Diese Implementierung ist in keinem Fall schlechter als Deine, nutzt aber die "copy elision"-Optimierung bei modernen Compilern aus.

    Ok - man lernt ja nie aus. Und was ist der Unterschied obiger Variante zu

    template <class T>
    std::vector<T> operator +( std::vector<T> SammelVektor, const std::vector<T>& Vektor2)
    {
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    ? - da war doch was, dass dann RVO nicht mehr gehen soll. Habe ich mal gehört/gelesen.

    Ja, so kann NRVO nicht mehr funktionieren. Aber in C++11 wäre das so okay. Da wird dann im Notfall eben move-konstruiert. Es kommt in C++11 also auf fast das gleiche raus.


  • Mod

    Werner_logoff schrieb:

    Und was ist der Unterschied obiger Variante zu

    template <class T>
    std::vector<T> operator +( std::vector<T> SammelVektor, const std::vector<T>& Vektor2)
    {
        SammelVektor.insert(SammelVektor.end(),Vektor2.begin(), Vektor2.end());
        return SammelVektor;
    }
    

    ? - da war doch was, dass dann RVO nicht mehr gehen soll. Habe ich mal gehört/gelesen.

    Das hat einen technischen Hintergrund (der Standard erlaubt NRVO auch in diesen Fällen). Für die Speicherresevierung von Funktionsparametern und dem Rückgabewert ist normalerweise der Aufrufer zuständig (weil diese ja über die Dauer des Funktionsaufrufes hinaus leben - die Parameter vor Aufruf, der Rückgabewert noch danach). Um nun NRVO auch für Funktionsparameter zu ermöglichen, müsste also der Aufrufer bereits vor dem Aufruf wissen, dass der jeweilige Parameter auch das Rückgabeobjekt sein wird. Das ist im Prinzip nur dann möglich, wenn diesem der Code der aufgerufenen Funktion bekannt ist. Erfahrungsgemäß führen Compiler aber selbst dann diese Optimierung nicht durch, wenn die Funktion geinlined wird (sollte die Kopie in Einzelfällen dann doch vermieden werden, dürfte das eher eine echte as-if-Optimierung sein).


Anmelden zum Antworten