c# IEnumerator in c++
-
Nexus schrieb:
Das ist dann aber eher eine Range als ein Iterator.
Ist mir schon klar - darauf wollte ich ja hinaus.

Gibt es auch schon in ähnlicher Form als boost::iterator_range.
Ja, kenne ich.
Edit: Bei näherer Betrachtung ist das Design hässlich. const-correctness sehe ich auch nirgends..
-
Hier noch ein paar Kommentare zu dem, was man hier gerade sehen konnte:
@nwp3:
- Ich finde es unschön, dass Dein Klassentemplate einen Containertypen als Templateargument statt eines Iterator-Typen erwartet. So hast du bei rohen Arrays z.B. je Größe N verschiedene Typen (
Iterator<T[N]>). Es gibt auch noch "scary iterators", also Iteratoren gleichen Typs von verschiedenartigen Containern. Zum Beispiel interessiert dich beim Iterieren nicht mehr, as für ein Allokator verwendet wurde, also sollte davon auch nix im Typ zu sehen sein. Andernfalls kann der Template-Bloat unnötig groß werden. - Dein
operator boolist nichtexplicit - Du verwendest c.begin(), c.end() statt begin(c), end(c). Klappt dann auch gar nicht auf rohen Arrays
operator boolist bei dir auch nichtexplicit- Im Wesentlichen ist das sehr ähnlich zu dem, was ich vorher schon verlinkt hatte -- mal von der Schnittstelle des "Iterators" abgesehen (
hasNextstattoperator bool, etc etc etc) - Du qualifizierst begin und end und schaltest damit ADL aus. Das ist ungeschickt. Deswegen wirst du u.a. "using std::begin" in dem finden, was ich verlinkte.
gruß,
kk
- Ich finde es unschön, dass Dein Klassentemplate einen Containertypen als Templateargument statt eines Iterator-Typen erwartet. So hast du bei rohen Arrays z.B. je Größe N verschiedene Typen (
-
Du qualifizierst begin und end und schaltest damit ADL aus. Das ist ungeschickt.
Wieso?
std::beginfindet es trotzdem durch SFINAE.operator bool ist bei dir auch nicht explicit
Das ist Absicht. Wieso sollte ich
bool(iter)in einer Bedingung stehen haben?Wie gesagt, ich finde mein Design nicht sehr hübsch, auf den zweiten Blick. Aber nicht aus diesen Gründen.
-
Sone schrieb:
Du qualifizierst begin und end und schaltest damit ADL aus. Das ist ungeschickt.
Wieso?
std::beginfindet es trotzdem durch SFINAE.Die Begründung hast du beim Zitat weggelassen. Es geht um ADL. Dadurch, dass du
beginundendmitstd::qualifizierst, beschränkst du Dich aufbegin/end-Funktionen, die imstd-Namensraum deklariert wurden. Damit sind anderebegin/endFunktionen, die woanders deklariert wurden und ohne Qualifizierung über ADL hätten gefunden werden können, für Dich unerreichbar. Interessant wird das für Typen, die keine begin/end-Elementfunktionen besitzen, aber freie begin/end-Funktionen imselben Namensraum anbieten.Sone schrieb:
operator bool ist bei dir auch nicht explicit
Das ist Absicht. Wieso sollte ich
bool(iter)in einer Bedingung stehen haben?Da liegt bei dir wohl ein Missverständnis vor. Ein
explicit operator bool()zwingt Dich nicht in Bedingungen,bool(iter)zu schreiben. Es unterdrückt lediglich diese blöden Situationen(*), die man sich früher nur mit dem "safe bool"-Idiom vom Hals halten konnte. Das C++11 Feature "explicit conversion operators" gibt es genau aus diesem Grund. Du wirst feststellen, dass std::unique_ptr und std::shared_ptr auch so einenexplicit operator bool()haben. Boolsche Kontexte wie beiif,while,&&u.s.w. kommen auch mit einemexplicit operator bool()klar.(* blöd:
deiniterator << 3; // upps, das kompiliert ja sogar!)
-
@nwp3, @Sone:
Vielen dank, ist so im groben das, was ich mir zuerst auch so zurecht gefrickelt hatte

@krümelkacker:
Man kann eben nicht so leicht über seinen Schatten springen. Ich mach' jetzt seit über 10 Jahren in C# und OOP sitzt einem da tief in den Knochen. Generika sind in C# eher mittel zum Zweck und eine ordentliche Grundlage um OO zu programmieren. Habe in der Zwischenzeit was implementiert, was meinem Bedürfnis nach Objectorientierung entgegenkommt. Ich wusste nicht, das sowas in der Art ``type erasure´´ heißt (falls ich das richtig verstanden habe). Das ist alles andere als produktionsreifer Code, über die grossen 3/5 habe ich mir im zusammenspiel mit unique_ptr<> auch noch nicht wirklich Gedanken gemacht, aber hey C#/Windows@work & C++/Linux/Embedded@home und wenn mir das auf meinem Mikrocontroller Probleme macht wird's halt verbessert.
Habe hier 'nen Link zu 'nem ähnlich alten Knochen wie mich gefunden:
http://www.artima.com/cppsource/type_erasure.html
Der sieht das ganze auch eher entspannt.#pragma once #include <memory> namespace kp { namespace lang { template<typename T> class Iter { public: typedef T value_type; typedef T& reference; class IterImpl; Iter(IterImpl* impl) : m_impl(impl) { } template<typename container> static Iter<T> create(container& cont) { return Iter(new ContainerIter<container>(cont.begin(), cont.end())); } template<typename container> static Iter<T> create(container& cont, typename container::iterator begin, typename container::iterator end) { return Iter(new ContainerIter<container>(begin, end)); } operator bool() const { return m_impl->valid(); } reference operator*() { return m_impl->current(); } reference operator->() { return m_impl->current(); } bool operator++() { return m_impl->next(); } class IterImpl { public: virtual bool valid() const = 0; virtual reference current() = 0; virtual bool next() = 0; virtual ~IterImpl() {} }; template<typename container> class ContainerIter : public IterImpl { public: typedef typename container::iterator iterator; ContainerIter(iterator begin, iterator end) : m_init(false), m_begin(begin), m_end(end) { } bool valid() const override { return m_init && m_current != m_end; } reference current() override { return *m_current; } bool next() override { if (m_init && m_current != m_end) { ++m_current; } else { m_current = m_begin; m_init = true; } return m_current != m_end; } private: bool m_init; iterator m_begin; iterator m_end; iterator m_current; }; private: std::unique_ptr<IterImpl> m_impl; }; } }
-
Oh? Ich dachte, in Bedingungen wie bei
if-Statements muss das Argument implizit nachboolkonvertiert werden. Vielen Dank für die Aufklärung.Die Begründung hast du beim Zitat weggelassen. Es geht um ADL. Dadurch, dass du begin und end mit std:: qualifizierst, beschränkst du Dich auf begin/end-Funktionen, die im std-Namensraum deklariert wurden. Damit sind andere begin/end Funktionen, die woanders deklariert wurden und ohne Qualifizierung über ADL hätten gefunden werden können, für Dich unerreichbar. Interessant wird das für Typen, die keine begin/end-Elementfunktionen besitzen, aber freie begin/end-Funktionen imselben Namensraum anbieten.
Ach, Quatsch! Da habe ich was missverstanden! Ich dachte, du meinst das begin/end , welches ich als Memberfunktion deklariert habe...
Mir ist durchaus klar, was es mit dem ADL-Problem vonstd::swapundstd::begin/endauf sich hat. Ich vergesse es nur immer wieder.Dass ich das
begin/endim Konstruktor vonIteratorunqualifiziert nutzen sollte, habe ich übersehen, auch Danke dafür, korrigiere ich gleich.
-
@knutjelitto: Wie gesagt, du versuchst zwei orthogonale Dinge aufeinmal zu machen. Wie gesagt, es ist besser, diese zwei Dinge zu trennen. Einmal ein leichtgewichtiges Objekt, was zwei C++-Iteratoren (ohne Type Erasure) kapselt und eine Schnittstelle anbietet, die dir besser gefällt (ich rate aber immer noch davon ab, schau dir mal Boost.Range an) und zum anderen willst Du ggf. (aber nicht oft!) den dynamischen Typen (und damit die Info, was für ein Container das war) verstecken. Das lässt sich separat lösen. Auch bei dir sollte der Konvertierungsoperator
explicitsein. Da könnte man sogar glatt eine Compiler-Warnung draus machen. Wenn Du im C++11-Modus schreibst... operator bool() const { ... ...dann sollte da höchstwahrscheinlich wirklich
... explicit operator bool() const { ... ...stehen. Dann überlädst du operator++ noch auf eine Art, die nichts mit dem zu tun hat, was man von operator++ normalerweise erwartet. Von operator++ erwartet man nicht einen boolschen Wert zurück.
Versuch mal Type Erasure als Ergänzung (nicht Modifikation) zu dem von mir verlinkten Code-Beispiel zu schreiben, also ein neues Klassen-Template
any_java_iter, so dass einany_java_iter<int>einjava_iter_wrapper<std::vector<int>::iterator>kapseln kann. -- Aber es sieht eigentlich so aus, als ob du damit keine Schwierigkeiten mehr haben solltest.
Sone schrieb:
Vielen Dank für die Aufklärung.
No problem.

-
Sone schrieb:
Oh? Ich dachte, in Bedingungen wie bei
if-Statements muss das Argument implizit nachboolkonvertiert werden. Vielen Dank für die Aufklärung.Kapitel 4 Absatz 3 Satz 2 f.
-
explicit - ich fummel hier im Augenblick mit MSVC++ rum, da kann man nicht so wirklich C++11 Sachen machen

Operator++ - war 'ne Kurzschlusshandlung. Habe mich am gebrauch orientiert, und bisher nur
if (++it) ...oder
while (++it) ...verwendet.
Habe den Code erstmal so runtergeschrieben. Ist noch nicht wirklich so strukturiert, wie ich das haben möchte. Und es zweistufig zu machen ist wohl 'ne gute Idee.
-
camper schrieb:
Kapitel 4 Absatz 3 Satz 2 f.
Ich war zuerst völlig verwirrt, aber dann habe ich selbst nachgeschlagen:
§6.4 schrieb:
The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch
Und dann... §4/3:
Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t (8.5).
Hätte ich auch selbst nachschlagen können :p
-
upps, habe statt auf edit einfach auf zitieren geklickt. hier gibt's nix zu sehen, habe nur auf seite 2 einen hinweis bzgl scary iterators hinzugefügt.