std::vector befüllen ohne copy constructor?
-
Skym0sh0 schrieb:
Einzig Move-Semantics und RValue-Referenzen bringen Performance**.
…
**: Das ist eine harte Aussage, aber so nicht gemeint. C++11 bringt schon viel, aber ich zweifle an, dass eine komplette Refaktorisierung eines großen Codestücks so viel Nutzen bringt. RValues bringen der Performance noch was, aber alles andere dürfte nur Syntax Zucker sein, oder?
Es war schon vorher in C++ möglich, verflixt schnelle Programme zu basteln. Es war auch schon in C möglich, sogar in Assembler. Allein es ist in C angenehmer als in Assembler, in altem C++ viel angenehmer als in C, und in C++11 (vor allem wegen range based for) angenehmer als in altem C++.
-
Ja, das stimmt.
-
Skym0sh0 schrieb:
(oder ähnliches)
Und hier liegt der Hund begraben: Der end-Iterator wird bei deiner Schleife potenziell in jedem Durchlauf angefordert, während bei range-based for dieser (hier
__end
) zwischengespeichert wird:{ auto && __range = range-init; for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) { for-range-declaration = *__begin; statement } }
Darüber hatten wir hier auch schon mal einen Thread.
-
@Arcoth: Stimmt. Trotzdem wählen Leute die Range-Based For primär wegen der Syntax. Dass dabei die Performance leicht besser sein kann, ist eher ein netter Nebeneffekt, vielen Entwicklern dürfte das gar nicht bekannt sein.
-
cooky451 schrieb:
Ohne C++11 willst du eh kein C++ benutzen, dann nimm lieber [beliebige andere Sprache].
So langsam muss man hier wirklich darauf achten, welche Antworten man ernst nehmen kann und welche nicht.
-
cl90 schrieb:
Wie kann man einen std::vector befüllen ohne das ein copy constructor aufgerufen wird? möglichst ohne etwas auf den Heap zu laden.
Ich würde gerne folgende Zeile, die noch einen copy constructor aufruft, austauschen.vector.push_back(QSubPlotable(&m_ColPattern, &m_FGWidthOffset))
Wie immer: Mehr Information wär nett gewesen. Aber ich rate mal, dass
QSubPlotable
ein Typ und keine Funktion ist und dass Deinevector
-Variable vom Typvector<QSubPlotable>
ist.Um etwas direkt in verschiedenen Containern zu erzeugen, ohne da etwas reinkopieren zu müssen, wurde in C++11
emplace
hinzugefügt:vector.emplace_back(&m_ColPattern, &m_FGWidthOffset);
Super wär's jetzt noch, wenn
QSubPlotable
movable würde (falls es das nicht schon ist). Dann könnte sich so ein Vektor auch relativ effizient vergrößern. Ansonnsten: Wenn du die Größe exakt kennst, kannst du natürlich auch vor den ganzen push/emplace_backs schonvector<>::reserve
aufrufen.Wenn ein
QSubPlotable
jetzt etwas großes, unhandliches ist, was einen teuren Copy-Ctor und keinen Move-Ctor hat, könnte man es ja dann noch mit einer Indirektion versuchen, alsounique_ptr<QSubPlotable>
oder so.Auch wichtig: Den Kram messen, wenn es um Geschwindigkeit geht.
-
kkaw schrieb:
cl90 schrieb:
Wie kann man einen std::vector befüllen ohne das ein copy constructor aufgerufen wird? möglichst ohne etwas auf den Heap zu laden.
Ich würde gerne folgende Zeile, die noch einen copy constructor aufruft, austauschen.vector.push_back(QSubPlotable(&m_ColPattern, &m_FGWidthOffset))
Wie immer: Mehr Information wär nett gewesen. Aber ich rate mal, dass
QSubPlotable
ein Typ und keine Funktion ist und dass Deinevector
-Variable vom Typvector<QSubPlotable>
ist.Um etwas direkt in verschiedenen Containern zu erzeugen, ohne da etwas reinkopieren zu müssen, wurde in C++11
emplace
hinzugefügt:vector.emplace_back(&m_ColPattern, &m_FGWidthOffset);
Super wär's jetzt noch, wenn
QSubPlotable
movable würde (falls es das nicht schon ist). Dann könnte sich so ein Vektor auch relativ effizient vergrößern. Ansonnsten: Wenn du die Größe exakt kennst, kannst du natürlich auch vor den ganzen push/emplace_backs schonvector<>::reserve
aufrufen.Wenn ein
QSubPlotable
jetzt etwas großes, unhandliches ist, was einen teuren Copy-Ctor und keinen Move-Ctor hat, könnte man es ja dann noch mit einer Indirektion versuchen, alsounique_ptr<QSubPlotable>
oder so.Auch wichtig: Den Kram messen, wenn es um Geschwindigkeit geht.
Ich hab es jetzt letztendlich auf dem Heap gelöst. also
vector.pushback(new bla());
mit entsprechenden deletes an anderen stellen.
Danke für die Ausführung. Ich werde mir das zu einem späteren Zeitpunkt zusammen mit c++11 wieder ansehen.
-
cl90 schrieb:
Ich hab es jetzt letztendlich auf dem Heap gelöst. also
vector.pushback(new bla());
mit entsprechenden deletes an anderen stellen.Och nö! Der Ansatz ist sehr fehleranfällig bzgl Speicherlecks. Dann nimm doch wenigstens
boost::ptr_vector
, wenn du schon keine C++11 Features benutzen willst/kannst.
-
kkaw schrieb:
cl90 schrieb:
Ich hab es jetzt letztendlich auf dem Heap gelöst. also
vector.pushback(new bla());
mit entsprechenden deletes an anderen stellen.Och nö! Der Ansatz ist sehr fehleranfällig bzgl Speicherlecks. Dann nimm doch wenigstens
boost::ptr_vector
, wenn du schon keine C++11 Features benutzen willst/kannst.Ich bin auch nicht zufrieden damit...
Aber sag mir, kann ich mein Projekt ohne viel aufwand einfach auf C++11 umstellen?
Ich habe jetzt schon so viele externe Abhängigkeiten und weit mehr als 5000 Zeilen code in über 80 files... Ich weiß nicht wie einfach man das umstellen kann.
Ich habs mir vorerst nur auf meien ToCheck Liste für das nächste Projekt gesetzt.prt_vector regelt das delete selbst?
Weil das wäre kein Problem. Ich hab Boost x64 bereits im Projekt.
-
Du musst nichts umstellen (ausser vielleicht den Compiler), C++11 ist abwärtskompatibel -- von unbedeutenden Features mal abgesehen.
Ja,
boost::ptr_vector
räumt selbst auf. Ich persönlich bin aber spätestens seit C++11 kein Fan mehr von den Pointer-Containern, siehe hier.
-
ok dann werd ich mal lesen wie das geht mit dem umstieg.
-
Nexus schrieb:
Ja,
boost::ptr_vector
räumt selbst auf. Ich persönlich bin aber spätestens seit C++11 kein Fan mehr von den Pointer-Containern, siehe hier....wobei ich im Falle von
vector<unique_ptr<T>>
die extra Indirektion, die man da durchführen muss, schon nervig finde. Das const wird auch leider nicht propagiert:void foo(vector<unique_ptr<int>> const& vec) { if (!vec.empty()) { *vec.front() += 17; // klappt! } }
Wenn Du "unhandliche Objekte" nur deswegen einzeln im Heap anlegst und die Adresse in einem unique_ptr speicherst, damit das Speichern im Vektor "effizienter" wird, ist diese Zeiger-Semantik eigentlich nicht das, was man haben will.
Klar, ich kann mir ausgehend von
vector<unique_ptr<T>>
immer noch perboost::indirect_iterator
zwei Iteratoren bauen, wo ich diese nervige extra Indirektion nicht mehr brauche und worüber auch einconst
hinzugefügt werden kann. Das ist schon mal was.Aber vielleicht wäre da ein anderer Wrapper nicht ganz verkehrt. Ich habe in meiner Sammlung noch eine Kuh (copy-on-write). Hier nur mal eine vereinfachte und ungetestete Skizze davon:
template<class T> class cow { public: cow() { static const auto def = make_shared<T>(); // cached default ptr = def; } cow(T const& x) : ptr(make_shared<T>(x)) {} cow(T && x) : ptr(make_shared<T>(std::move(x))) {} template<class U, class...More ,class=typename std::enable_if< !std::is_convertible<U,cow const&>::value && std::is_constructible<T,U,More...>::value >::type > cow(U && x, More && ... xx) : ptr(make_shared<T>(forward<U>(x),forward<More>(xx)) {} T const& read() const {return *ptr;} T const& operator*() const {return *ptr;} T const* operator->() const {return ptr.get();} operator T const&() const& {return *ptr;} operator T const&() const&& = delete; // zu unsicher T& write() { if (!ptr.unique()) { ptr = make_shared<T>(*ptr); } assert(ptr.unique()); return *ptr; } private: shared_ptr<T> ptr; };
was man dann so nutzen könnte:
vector<cow<unhandlicher_typ>> vec; vec.emplace_back(dies,und,jenes); ... for (auto& x : vec) { x.write().veraendere_dich(1,2,3); } for (unhandlicher_typ const& x : vec) { cout << x.get_something() << endl; }
Und sonst könnte man ja auch einfach mal
std::deque<T>
mitemplace
ausprobieren ohneunique_ptr
und ohnecow
. Da sollte auch nicht so viel kopiert werden. Und das wäre mein aktueller Tipp für cl90: Probier malstd::deque
und/oderemplace
aus.cl90 schrieb:
ok dann werd ich mal lesen wie das geht mit dem umstieg.
Ich glaube, beim Microsoft Compiler musst du gar nix besonderes machen. Er unterstützt automatisch schon C++11 Features, sofern einigermaßen aktuell. Beim GCC kommt es auf die Version an. Ggf brauchst du den Compilerschalter "-std=c++0x" oder "-std=c++11".