Kann man den Operator + bei vector<> überladen?
-
Hallo!
Ich stehe vor einem Problem.
Ich habe in einer Klasse einige Vektoren. Zum Beispiel:vector<int> a (4,0xFF); vector<int> b (2,0x00); vector<int> c; vector<int> d; vector<int> e;Ich will bzw. muss diese Vektoren an unterschiedlichen Stellen in der Klasse zu einem neuen Vektor zusammensetzen können. Und das in beliebiger Reihenfolge.
Bisher mache ich das so:
vector<int> z; z.insert(z.end(), b.begin(), b.end()); z.insert(z.end(), e.begin(), e.end()); z.insert(z.end(), b.begin(), b.end());usw. usw. usw.
Viel lieber wäre mir aber solch eine Lösung:vector<int> z; z = b + e + b;Jetzt endlich meine Frage

Kann ich den + Operator bei Vektoren überladen? Ich habe es schon auf die ein oder andere Weise probiert. Aber entweder bin ich da zu blöde für, oder es geht einfach nicht.Solche Konstrukte hatte ich bisher in der Klasse definiert:
std::vector<int>& operator+(const std::vector<int>&); std::vector<int>& operator+(const std::vector<int>); std::vector<int> operator+(const std::vector<int>); std::vector<int> operator+(const std::vector<int>&, const std::vector<int>&);Aber das hat alles nur diverse Fehler produziert.
Ich gebe ehrlich zu, dass mit dem Überladen von Operatoren habe ich noch nicht so richtig verstanden. Allerdings muss ich bis Dienstag Abend irgendwie eine Lösung finden.

Bitte, kann einer von euch mir helfen?
Vielen lieben Dank!!Gruß
Thorsten
-
Etwa so:
template<class T> vector<T> operator + (const vector<T>& v1, const vector<T>& v2) { vector<T> v = v1; v.insert(v.end(), v2.begin(), v2.end()); return v; }Dann musst du nur noch hoffen, dass dein Compiler Copy-Elison bzw. NRVO gut beherrscht

Edit: falls er das nicht tut, kann man sich eventuell sowas basteln:
template<class T> vector<T>& operator += (vector<T>& to, const vector<T>& from) { to.insert(to.end(), from.begin(), from.end()); return to; } // ... ((z += b) += e) += b;Das ist zumindest etwas übersichtlicher.
-
Das sollte eigentlich gehen, so wie du es versucht hast:
#include<vector> #include<iostream> std::vector<int> operator+(const std::vector<int>& lhs, const std::vector<int>& rhs){ std::vector<int> temp(lhs); temp.insert(temp.end(),rhs.begin(),rhs.end()); return temp; } int main() { std::vector<int> A (3,100); std::vector<int> B (0,200); std::vector<int> C = A+B; for (int i=0; i<C.size(); ++i) std::cout<<C[i]<<std::endl; }Natürlich noch schicker mit Templateparameter, anstatt nur für int.
edit: Oha, 10 Minuten zu spät. Ich sollte a) Beispiele schneller programmieren und b) mal prüfen ob inzwischen schon jemand anderes geantwortet hat, bevor ich abschicke.
-
operator+ finde ich eigentlich unpassend, da a+b != b+a
(ok ist bei strings auch so. Aber da finde ich + auch unpassend)template<typename T, class Alloc> vector<T, Alloc> operator+(vector<T, Alloc> lhs, vector<T, Alloc> const &rhs) { lhs.reserve(lhs.size() + rhs.size()); lhs.insert(lhs.end(), rhs.begin(), rhs.end()); return lhs; }
-
@rüdiger:
Warum verändert deine Funktion ein Argument? - Für den +-Operator würde ich da eher die Variante von ipsec bevorzugen, was auch eher dem Verhalten vonstd::stringentspricht.Ich find den Operator bei strings gar nicht so unpassend, denn ein solches addieren ist recht intuitiv verständlich, dass dort ein String angehängt wird.
Dass da nicht irgendwie eine Zeichenweise Addition statt findet ist denke ich recht klar.Bei dem gezeigtem kann man sich natürlich streiten, aber dazu müssten wir noch mehr über den Hintergrund wissen.
-
SeppJ schrieb:
edit: Oha, 10 Minuten zu spät. Ich sollte a) Beispiele schneller programmieren und b) mal prüfen ob inzwischen schon jemand anderes geantwortet hat, bevor ich abschicke.
Nein, fang sowas nicht an. Meistens ist die zweite Lösung doch in einem kleinen Detail anders, und dem Fragesteller ist durchaus damit gedient, verschiedene Stilrichtungen zu sehen.
Wenns im Prinzip nichts Neues ist, ist ein
edit: zu spät
sehr schick, um dem Erstantworter und allen anderen noch schnell zu sagen, daß man damit nicht widersprechen wollte und man auf die leicht andere Gangart nicht pocht.
-
Also ich hatte Addition noch nie wirklich mit Assoziativität verbunden, daher stört mich das nicht. Jedoch würde ich bei Addition von vectoren intuitiv eher eine elementweise Addition erwarten. Den hier überladenen Operator sollte man also mit Bedacht einsetzen und gut dokumentieren. Mir fällt auch gerade kein gutes Operatorsymbol für eine Union zweier Mengen ein, da ich eigentlich überall elementweise Operationen erwarten würde. Am ehesten noch der Linksshift-Operator <<. Die Schreibweise a<<b würde mich dann nämlich an die iostreams erinnern, wo damit auch angehängt wird.
@volkard: Ok, werde ich dran denken. Beziehungsweise einfach nichts anders machen als bisher.

-
drakon schrieb:
@rüdiger:
Warum verändert deine Funktion ein Argument? - Für den +-Operator würde ich da eher die Variante von ipsec bevorzugen, was auch eher dem Verhalten vonstd::stringentspricht.Das erste Argument wird kopiert. Teilweise ist es für den Compiler einfacher, unnötige Kopien wegzuoptimieren, wenn die Kopie implizit (über Parameter oder Rückgabewert) erfolgt.
Das Verhalten von rüdigers Funktion ist analog zum
operator+beistd::string.
-
Oh, krass! Das ging jetzt aber wahnsinnig schnell! O.O
Habe spontan die Lösung von SeppJ ausprobiert, aber das läuft natürlich nicht bei mir. Also zumindest nicht innerhalb einer Klasse. Ich habe stellenweise echt Wissenslücken in C++.

In file included from main.cpp:2: test.h:12: error: ‘std::vector<int, std::allocator<int> > Test::operator+(const std::vector<int, std::allocator<int> >&, const std::vector<int, std::allocator<int> >&)’ must take either zero or one argumentLiegt bestimmt wieder an der späten Stunde und meiner Doofheit. Also das hier ist die test.h:
#ifndef TEST_H #define TEST_H #include <vector> class Test { public: Test(); ~Test(); std::vector<int> operator+(const std::vector<int>& lhs, const std::vector<int>& rhs); void testop(); }; #endifUnd das die passende test.cpp:
#include <vector> #include <iostream> #include "test.h" std::vector<int> operator+(const std::vector<int>& lhs, const std::vector<int>& rhs){ std::vector<int> temp(lhs); temp.insert(temp.end(),rhs.begin(),rhs.end()); return temp; } void Test::testop() { std::vector<int> a (2,0xDD); std::vector<int> b (2,0xAA); std::vector<int> c ; c = a + b; }Gruß
Thorsten
-
Du musst den Operator als freie Funktion und nicht als Memberfunktion implementieren.
-
SeppJ schrieb:
Jedoch würde ich bei Addition von vectoren intuitiv eher eine elementweise Addition erwarten.
Ich weiß nicht. vector ist so eine eierlegende Wollmilchsau.
Die elementweise Addition würde ich gerne bei valarray sehen. valvector?
-
Nexus schrieb:
drakon schrieb:
@rüdiger:
Warum verändert deine Funktion ein Argument? - Für den +-Operator würde ich da eher die Variante von ipsec bevorzugen, was auch eher dem Verhalten vonstd::stringentspricht.Das erste Argument wird kopiert. Teilweise ist es für den Compiler einfacher, unnötige Kopien wegzuoptimieren, wenn die Kopie implizit (über Parameter oder Rückgabewert) erfolgt.
Das Verhalten von rüdigers Funktion ist analog zum
operator+beistd::string.Stimmt. Hab da irgendwie ein & gesehen.
Elementweise Addition fände ich auch hier eher merkwürdig.
-
SeppJ schrieb:
Mir fällt auch gerade kein gutes Operatorsymbol für eine Union zweier Mengen ein, da ich eigentlich überall elementweise Operationen erwarten würde.
Wie wäre es mit
operator&oderoperator,? (Natürlich sollte man zu beiden noch einen Kommentar schreiben)
-
Aber gilt der Operator dann nicht für alle + Vector-Operatoren? Ich würde das ganze gerne auf eine spezielle Klasse beschränken. Der ganze Quatsch wird später mal Teil eines größeren Projektes, wo auch andere dran arbeiten. Will denen nicht meinen Operator aufdrängen.
Aber ansonsten läuft es super. Vielen lieben Dank für eure Hilfe!!!
Gruß
Thorsten
-
Dann lass die Deklaration im Header und mach lediglich eine Definition in der Quelldatei. Andere Dateien haben da dann keinen Zugriff drauf.
Alternativ wäre es noch eine Möglichkeit das in einen namespace zu packen, aber ich denke, dass das nicht nötig ist.
-
Nexus schrieb:
drakon schrieb:
@rüdiger:
Warum verändert deine Funktion ein Argument? - Für den +-Operator würde ich da eher die Variante von ipsec bevorzugen, was auch eher dem Verhalten vonstd::stringentspricht.Das erste Argument wird kopiert. Teilweise ist es für den Compiler einfacher, unnötige Kopien wegzuoptimieren, wenn die Kopie implizit (über Parameter oder Rückgabewert) erfolgt.
Das Verhalten von rüdigers Funktion ist analog zum
operator+beistd::string.ergänzend noch folgender Artikel
http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
-
rüdiger schrieb:
operator+ finde ich eigentlich unpassend, da a+b != b+a
(ok ist bei strings auch so. Aber da finde ich + auch unpassend)template<typename T, class Alloc> vector<T, Alloc> operator+(vector<T, Alloc> lhs, vector<T, Alloc> const &rhs) { lhs.reserve(lhs.size() + rhs.size()); lhs.insert(lhs.end(), rhs.begin(), rhs.end()); return lhs; }Ich denke nicht, dass
reservenotwendig ist. Ich würde mal davon ausgehen, dass, falls insert RandomAccess-Iteratoren bekommt, es selbst schlau genug ist, entsprechend Platz zu machen. Das geht ja eigentlich recht einfach über iterator_traits<Iter>::iterator_category und Tag-Dispatching. Gut, garantiert wird dies wahrscheinlich nicht, aber es macht IMHO eine gute StdLib-Implementierung aus. (Ich hab's in diesem Fall nicht wirklich getestet, aber ich weiß, dass zB in libstdc++ derartige Optimierungen en masse enthalten sind. std::copy kann z.B. u.U. auch zu einem memmove reduziert werden).Bzgl Performanz/Optimierung geht sogar noch einen Tucken besser:
template<typename T, class Alloc> vector<T, Alloc> operator+(vector<T, Alloc> lhs, vector<T, Alloc> const &rhs) { vector<T,Alloc> result; result.swap(lhs); result.insert(result.end(), rhs.begin(), rhs.end()); return result; // NRVO möglich }Jedoch werden die Elemente bei der Zuweisung in
c = a+b;noch unnötig kopiert. Da man vector<> nicht verändern kann, geht das meines Wissens nach auch nicht anders -- es sei denn, man arbeitet im C++0x Modus. Dann findet natürlich kein unnötiges Kopieren statt, weil die Elemente von(a+b)einfach nachc"umziehen".kk
-
shinji schrieb:
Viel lieber wäre mir aber solch eine Lösung:
vector<int> z; z = b + e + b;Die unnötige Kopie, von der ich vorhin sprach, lässt sich aber auch so umgehen:
vector<int> z = b + e + b;Tipp: Vermeide Definition mit Defaultinitialisierung + Zuweisung. Benutze gleich die richtige Initialisierung. Hier kann ein Compiler die unnötige temporäre Kopie, die sich aus (b+e+b) ergibt, wegoptimieren.
kk
-
also vektoren mit "+" aneinanderhängen ist doch sowieso schmarrn.
nicht umsonst nimmt man in java/c#/... einen string-builder statt dauernd strings mit "+" zusammenzuhängen.
-
hustbaer schrieb:
also vektoren mit "+" aneinanderhängen ist doch sowieso schmarrn.
nicht umsonst nimmt man in java/c#/... einen string-builder statt dauernd strings mit "+" zusammenzuhängen.Ich glaube nicht.
Man nimmt den Stringbuilder, weil er so toll wachsen kann wie ein std::vector oder std::string. Normale Strings können das nähmlich nicht.
Nur ist für viele Anfügungen der op+ nicht gut und der op+= ist besser.
-
drakon schrieb:
Alternativ wäre es noch eine Möglichkeit das in einen namespace zu packen, aber ich denke, dass das nicht nötig ist.
Gilt der Operator dann nur in dem namespace oder kann man dann folgendes schreiben:
std::vector<int> A, B, X; X = A std::+ B