C++11-Standard zu std::begin() und anderes
-
nwp3 schrieb:
1. Es gibt freie Funktionen begin und end. Diese sind zu bevorzugen, da sie relativ leicht für bestehende Container hinzugefügt werden können, deren Implementation nicht geändert werden kann oder soll. Diese reichen aus damit range based for funktioniert.
In C++03 war es nicht erlaubt Funktionen aus dem Namensraum
stdzu überladen. Dabei, dass Container gewöhnlicherweise aus Klassentemplates instanziiert wurden, sind wir uns höchstwahrscheinlich einig. Wie bietet man diese freiestd::begin-Funktion denn an? Muss man also - wie im Op erwähnt -Container.begin()anbieten, damit die Standardversion vonstd::begindarauf zugreifen kann?
-
Range-based-for-loops sind nur dann verfügbar, wenn std::begin() und std::end() für den Container verfügbar sind, oder?
Nein. Der Standard definiert range-based for loops nicht durch Bibliotheksabhängige Komponenten, das wäre vollkommen unnötig.
begin-expr and end-expr are determined as follows:
— if_RangeTis an array type, begin-expr and end-expr are__rangeand__range + __bound, respectively, where__boundis the array bound. If_RangeTis an array of unknown size or an array of incomplete type, the program is ill-formed;
— if_RangeTis a class type, the unqualified-ids begin and end are looked up in the scope of class_RangeTas if by class member access lookup (3.4.5), and if either (or both) finds at least one declaration, begin-expr and end-expr are__range.begin()and__range.end(), respectively;
— otherwise, begin-expr and end-expr arebegin(__range)andend(__range), respectively, wherebeginandendare looked up in the associated namespaces (3.4.2).
[ Note: Ordinary unqualified lookup (3.4.1) is not performed. — end note ]Wie du siehst müssen die Funktionen nicht Teil vom Namensraum
stdsein.Ups, hab gerade nachgelesen, dass das partielle Spezialisieren von Funktionstemplates in C++11 noch immer nicht erlaubt ist.
Das macht auch überhaupt keinen Sinn.
-
asfdlol schrieb:
Ich bin ein C++11-Nub und versuche mich im Moment ein bisschen daran.
Die Funktion nub gibt es leider noch nicht in C++, auch nicht C++11. Aber std::sort+std::unique ist meistens ausreichend.
1. Range-based-for-loops sind nur dann verfügbar, wenn
std::begin()undstd::end()für den Container verfügbar sind, oder?Nein. Siehe http://en.cppreference.com/w/cpp/language/range-for für das genaue Prozedere (ist schon ein bisschen absurd). Wichtig ist, dass nicht std::begin aufgerufen wird, sondern begin mit ADL.
Und ist es dann laut Standard erlaubt, diese Funktionen
(partiell) zu spezialisieren oderzu überladen? Wenn nicht, dann müssen sich alle (eigenen) Container an dieContainer.begin()- /Container.end()- (und mit C++14 dann auchContainer.rbegin()- /Container.rend()-) Signatur halten, nicht wahr?Du kannst freie Funktionen in deinem Namespace anbieten.
2. Wieso bieten
std::priority_queuesowie die geordneten assoziativen Container keine relationalen Operatoren an?std::map/std::set bieten das an. Bei std::priority_queue macht das keinen Sinn, weil man auch nicht über die Werte iterieren kann.
3. Übersehe ich einen offensichtlichen Grund wieso
std::iotaeine Funktion ist und nicht ein Funktor, geeignet fürstd::generate? Der Funktor wäre meiner Meinung nach flexibler da man diesen selbst wieder z.B. transformieren könnte.std::iota ist ein Fehldesign.
4. Was ist der Grund wieso man sich damals entschieden hat, dass man Zuweisungsoperatoren nicht global definieren darf? (ist bestimmt trivial und naheliegend, ich seh's aber im Moment wirklich nicht...)
Technische Gründe gibt es keine. "War schon immer so". Konstruktoren und Destruktoren kann man auch nicht global definieren, passt also mMn schon.
Wieso ich danach frage? Ich habe im Moment einen Fall, in welchem es mir zugutekommen würde, diesen Operator für Klassen aus der STL zu überladen (für ein wenig Syntax Sugar). Auch ansonsten kann ich mir ein paar schöne Fälle vorstellen (friend name injection).
Kennst do Boost.Assignment? Du kannst RHS überladen.
-
asfdlol schrieb:
4. Was ist der Grund wieso man sich damals entschieden hat, dass man Zuweisungsoperatoren nicht global definieren darf? (ist bestimmt trivial und naheliegend, ich seh's aber im Moment wirklich nicht...)
Das Problem hierbei ist, dass eine Verwendung des Zuweisungsoperators vor dieser Deklaration trotzdem zulässig sein müsste (in Form des Compiler generierten Operators). Aber dann wird die ODR verletzt. Ausserdem würde diese Deklaration beeinflussen, ob eine Klasse POD bzw. trivial kopierbar ist. Und das ist seltsam: man muss ja den Operator dann nicht in jeder ÜE überladen, sofern er nicht überall gebraucht wird.
Beim (unären) &-Operator besteht diese Problematik im Prinzip auch, allerdings wird dieser Operator nur selten überladen.
Für die anderen Operatoren, die als nicht-statische Member implementiert werden müssen () [] -> ist diese Beschränkung historisch bedingt. Stroustrup war ursprünglich der Ansicht, dass es technische Probleme geben würde. Wäre eigentlich schön, wenn man die auch für Aufzählungen überladen könnte. Oder die Invertierung von äußerem und innerem Operanden bei [] so wie es beim eingebauten Operator der Fall ist.
-
asfdlol schrieb:
3. Übersehe ich einen offensichtlichen Grund wieso
std::iotaeine Funktion ist und nicht ein Funktor, geeignet fürstd::generate? Der Funktor wäre meiner Meinung nach flexibler da man diesen selbst wieder z.B. transformieren könnte.std::iotaist insbesondere auch unnötig langsam, da man es nur auf bereits initialisiertem Speicher verwenden kann. Ich hätte die so implementiert, dass die ein paar von random access Iteratoren zurückliefert, die man direkt und effizient mit entsprechenden Kostruktoren oder insert Methoden verwenden kann. Das würde auch viel besser zum Konzept passen da ansonsten Ranges meist durch ein Paar von Iteratoren ausgedrückt werden.
-
std::iota ist insbesondere auch unnötig langsam, da man es nur auf bereits initialisiertem Speicher verwenden kann.
Schon mal was von
back_insertergehört?
-
Arcoth schrieb:
std::iota ist insbesondere auch unnötig langsam, da man es nur auf bereits initialisiertem Speicher verwenden kann.
Schon mal was von
back_insertergehört?back_inserter würde nur mit iota_n funktionieren (was es nicht gibt).
-
iota schrieb:
Arcoth schrieb:
Schon mal was von
back_insertergehört?back_inserter würde nur mit iota_n funktionieren (was es nicht gibt).
Und selbst ein
iota_nmitback_inserterwäre langsamer, da bei jedem Aufruf die size neu gesetzt werden müsste. Eine Reallokation ließe sich zwar mit einem Aufruf vonreserve()vermeiden, aber auch das ist unnötig umständlich und hässlich.
-
Das ist dann allerdings nicht
iota's Schuld sondern die vonvector- du kannst das wiederholte Setzen vonsize()nicht vermeiden. Dann eher mit rohem Speicher undraw_storage_iterator.Das mit den beiden Iteratoren ist übrigens bereits existent; siehe Boost.Range:
#include <boost/range/irange.hpp> int main() { auto r = boost::irange(1, 20); std::vector<int> v(r.begin(), r.end()); for (auto i : v) std::cout << i << ' '; }
-
Arcoth schrieb:
Das ist dann allerdings nicht
iota's Schuld sondern die vonvector- du kannst das wiederholte Setzen vonsize()nicht vermeiden.Doch kann ich. Wenn der Vector vorher bestimmen kann wie viele Einträge ich hinzufügen möchte, ist das nicht nötig. Dies leisten die Methoden
vector::vector(InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());und
iterator vector::insert (const_iterator position, InputIterator first, InputIterator last);Wenn man ihnen random access iteratoren übergibt.
Arcoth schrieb:
Das mit den beiden Iteratoren ist übrigens bereits existent; siehe Boost.Range:
#include <boost/range/irange.hpp> int main() { auto r = boost::irange(1, 20); std::vector<int> v(r.begin(), r.end()); for (auto i : v) std::cout << i << ' '; }Das ist in etw das was ich mir vorstelle, wobei Boost.Range mit dem Range-Concept noch einen Schritt weiter geht.
-
Um meine Position nochmals zu verdeutlichen. Neben den Performance- und Usability Aspekt finde ich das Interface von iota einfach semantisch fragwürdig. Was iota konzeptionell macht, ist einen range von integern zu generieren. Daher sollte es auch einen entsprechenden range liefern. Tatsächlich hat iota aber ein interface wie ein algorithmus der einen bestehenden range modifiziert. Das macht in meinen Augen überhaupt keinen Sinn. OK, mit std::fill existiert ein ähnlicher algorithmus, aber auch den finde ich recht unglücklich. Zudem sollte es dann eben aus Konsistenzgründen auch iota_n geben.
-
Arcoth schrieb:
[...]
Achso ok, danke. Da war also der Kern des Missverständnisses.
ghjkrofl

Sorry, ich hab die Operatoren für die assoziativen Container in der Referenz irgendwie übersehen...
ghjkrofl schrieb:
Kennst do Boost.Assignment? Du kannst RHS überladen.
Kannte ich nicht, nein. Sieht super schick und super elegant aus.
camper schrieb:
[...]
Wow, daran hab ich nicht gedacht.
Danke an alle.