end-Iterator decrement definiert?
-
Ihr diskutiert wirklich, wie man über ein Range iteriert?
Ich mein, die Antwort ist doch klar! Der einzige wahre Weg ist:void iterate(iterator begin, iterator end) { (do_sth(*begin), begin != end) && (iterate(++begin, end), true); }
Ich bitte euch!
-
Nathan schrieb:
Ihr diskutiert wirklich, wie man über ein Range iteriert?
Ich mein, die Antwort ist doch klar! Der einzige wahre Weg ist:void iterate(iterator begin, iterator end) { (do_sth(*begin), begin != end) && (iterate(++begin, end), true); }
Ich bitte euch!
UB wenn begin==end. Ich sags dir, über einen Range zu iterieren ist nicht trivial, deshalb ist man froh, die Funktion range() zu haben.
-
c++11-coder schrieb:
Nathan schrieb:
Ihr diskutiert wirklich, wie man über ein Range iteriert?
Ich mein, die Antwort ist doch klar! Der einzige wahre Weg ist:void iterate(iterator begin, iterator end) { (do_sth(*begin), begin != end) && (iterate(++begin, end), true); }
Ich bitte euch!
UB wenn begin==end. Ich sags dir, über einen Range zu iterieren ist nicht trivial, deshalb ist man froh, die Funktion range() zu haben.
Stimmt, falsche Reihenfolge
begin != end && (do_sth(*begin), iterate(++begin, end), true);
Mein Fehler.
-
Schade, ich dachte schon ihr wärt an einer ernsthaften Diskussion interessiert.
@Arcoth: Bitte bleib beim konkreten Beispiel und sag, wo dort die Vorteile an deiner Version liegen. Dein Stellvertreter-Argument kann ich so nicht übertragen. Ich denke aber es wäre einfacher, nachzugeben
-
Nathan schrieb:
Stimmt, falsche Reihenfolge
begin != end && (do_sth(*begin), iterate(++begin, end), true);
Mein Fehler.
Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
</OT>Nexus schrieb:
Schade, ich dachte schon ihr wärt an einer ernsthaften Diskussion interessiert.
Bin ich. Ich finde meine range-Variante kürzer und lesbarer.
Und zwar aus dem Grund, dass es auf den ersten Blick genauso kurz verständlich ist wie die explizite for-loop, aber man bei der for-Loop immer noch einen kurzen Check machen muss, ob die Form wirklich stimmt. Wie schnell merkst du, dass bei den folgenden Schleifen was faul ist:
for( unsigned i = 1; i == 20; ++i )
for( unsigned i = 1; i != 20; --i )
-
Sorry, Nexus aber:
c++11-coder schrieb:
Nathan schrieb:
Stimmt, falsche Reihenfolge
begin != end && (do_sth(*begin), iterate(++begin, end), true);
Mein Fehler.
Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
</OT>Die Tricks vom IOCCC funktionieren wohl wirklich nur mit C.
Nexus schrieb:
Schade, ich dachte schon ihr wärt an einer ernsthaften Diskussion interessiert.
Bin ich. Ich finde meine range-Variante kürzer und lesbarer.[...]
Absolut deiner Meinung.
-
Ich denke aber es wäre einfacher, nachzugeben
Jup, ich gebe nach. War alles Blödsinn. Auch wenn ich while-Schleifen einfach deutlich mehr mag, als for-Schleifen. Ich mag es halt nicht, wenn mehrere Dinge auf eine Zeile fallen, dann müssen schon viele Leerzeichen dabei sein...
-
Nathan schrieb:
Sorry, Nexus aber:
c++11-coder schrieb:
Nathan schrieb:
Stimmt, falsche Reihenfolge
begin != end && (do_sth(*begin), iterate(++begin, end), true);
Mein Fehler.
Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
</OT>Die Tricks vom IOCCC funktionieren wohl wirklich nur mit C.
cast nach void hilft.
-
c++11-coder, kannst du etwas mehr über
range()
verraten? Basiert das auf Boost.Range oder hast du das selbst gestrickt?Ich frage mich vor allem deswegen, weil
for (auto e : r)
davon ausgeht, dass für den Typ von
r
einbegin()
undend()
(global oder als Member) vorhanden ist, und diese beiden Funktionen jeweils Iteratoren zurückgeben.Damit aber so etwas möglich wäre wie in der verschachtelten Schleife, wo
e
selbst ein Iterator (und kein Element) ist, müsstest du eine zusätzliche Indirektion einbauen. Machst du das tatsächlich oder war es mehr eine Idee?
-
Ich habe mir mein range() zwar selber geschrieben, aber neu ist die Idee nicht. Du könntest auch boost::irange verwenden:
#include <iostream> #include <string> #include <boost/range/irange.hpp> int main() { for (int i : boost::irange(0,5)) std::cout << i << '\n'; std::string s = "abcdef"; using std::begin; using std::end; for (auto i : boost::irange(begin(s), end(s))) for (auto j : boost::irange(std::next(i), end(s))) std::cout << *i << *j << '\n'; }
Ausgabe:
0 1 2 3 4 ab ac ad ae af bc bd be bf cd ce cf de df ef
Dieser range() ist eine schöne Ergänzung zum normalen range based for, weil das for eine Indirektion wegnimmt, man aber manchmal die Iteratoren eben doch braucht. Und es funktioniert super mit int-Ranges.
-
Arcoth schrieb:
Ich denke aber es wäre einfacher, nachzugeben
Jup, ich gebe nach. War alles Blödsinn. Auch wenn ich while-Schleifen einfach deutlich mehr mag, als for-Schleifen. Ich mag es halt nicht, wenn mehrere Dinge auf eine Zeile fallen,
Du magst for nicht so gern, NUR WEIL Du for mißbrauchst, Alda!
-
volkard schrieb:
Du magst for nicht so gern, NUR WEIL Du for mißbrauchst, Alda!
Jetzt halt schon die Schnauze mit deinem Assi-Dialekt! Bist du erst 25 oder was?
Und ich missbrauche
for
-Schleifen nie! Ich nutze sie tatsächlich lediglich für das Standardschema. Beispiel:for( unsigned i = 2; i != primes.size(); ++i )
Für nichts anderes. Außer vielleicht
for( unsigned i; std::cin >> i; ) //...
Wobe ich da schon eher zu
while
tendiere.
-
Arcoth schrieb:
Und ich missbrauche
for
-Schleifen nie! Ich nutze sie tatsächlich lediglich für das Standardschema. Beispiel:for( unsigned i = 2; i != primes.size(); ++i )
Für nichts anderes.
Aha...
Arcoth schrieb:
for (auto first = begin(v); first != end(v); ++first) // Hier sollte die Abbruchbedingung wahrscheinlich eher first != --end(v) lauten for( auto second = first; ++second != end(v); ) doSomething( *first, *second );
Das ist mMn. deutlich eleganter.
-
Arcoth schrieb:
Allerdings kann man dieses temporäre Objekt gleich selbst in die Hand nehmen
… for( auto second = first; ++second != end(v); ) doSomething( *first, *second );
Das ist mMn. deutlich eleganter.
-
Aha...
Ups! Ich sollte es natürlich nicht so formulieren. Ich nutze es auch zum Iterieren mit Iteratoren. Standardschema hieß oben
for( Initialisieren; Vergleich; Inkrement )
wie immer.Außerdem habe ich nicht gesagt, dass
for( auto second = first; ++second != end(v); )
die eleganteste Methode ist. Es ist nur so: Ich finde es schöner als
for( auto second = std::next( first ); second != end(v); ++second )
,
aber wahrscheinlich würde ich in meinem Code eherwhile
benutzen. Und zwar soauto second = first; while( ++second != end(v) )
Weil ich for, sobald es von obigem Schema abweicht, einfach nicht mehr attraktiv finde. Bei while kann die Bedingung auch gut und gerne ein wenig komplizierter werden.
Nun fand ich bei dem konkreten Beispiel doof, dass man die Klammern ergänzen muss, wenn man
while
verwendet (weil man noch eine Zeile zum Definieren vonsecond
braucht)! Ich versuche immer möglichst ohne diese Klammern auszukommen - das ist ein Tick von mir
-
Arcoth schrieb:
for( auto second = std::next( first ); second != end(v); ++second )
Ein Bißchen Syntax-Rauschen weg
for( auto second=next(first); second!=end(v); ++second )
und schon isses hübsch.
Nee, mal ehrlich, eine komplexte Laufbedingung nervt Dich weniger als eine komplexe Initialisierung, das finde ich irgendwie C.
-
Arcoth schrieb:
...
for( auto second = first; ++second != end(v); )
Man sollte nur unter zwingenden Gründen von der Standard-Funktionalität der drei
for
-Argumente abweichen. Das "clevere"++second
ist eine versteckte Bombe für alle anderen (und dich selbst in 3 Jahren), die mit dem Code zu tun haben: Man muss erst einmal die volle Tragweite des ++ an dieser Stelle erfassen! Der Großteil der Leser wird auf den ersten Blick wohl nicht korrekt erfassen, das die Schleife tatsächlich tut.Geht in Richtung "Principle of least surprise". Da muss das persönliche Schönheitsempfinden manchmal weichen, vor allem, wenn man nicht allein entwickelt.
Arcoth schrieb:
auto second = first; while( ++second != end(v) )
... einfach nicht mehr attraktiv finde.
Dito. Jedes Handbuch über guten Code sagt, dass
for
nicht nur eine Art von Schleife ist, sondern bereits deren Aufgabe beschreibt, nämlich über eine bestimmte Menge der Reihe nach zu iterieren. Allein durch dieses Schlüsselwort hat der Leser bereits mehr Information als bei einem simplenwhile
. Du ziehst eine Mutter auch nicht mit der Kombizange an, wenn du den passenden Maulschlüssel hast, oder?Dazu kommt noch was anderes:
second
steht außerhalb des Scopes der Schleife, beifor
innerhalb.
-
Arcoth schrieb:
auto second = first; while( ++second != end(v) )
... einfach nicht mehr attraktiv finde.
Dito.
Lern lesen und zitieren, Junge.
Edit: Oder nur zitieren. Ich weiß nicht genau, zu was du zustimmst, aber das Zitat ist falsch.Das "clevere" ++second ist eine versteckte Bombe für alle anderen (und dich selbst in 3 Jahren), die mit dem Code zu tun haben:
Ich werde auch in dreißig Jahren sofort sehen, was
++second
da verloren hat, da bin ich mir verdammt sicher
-
Arcoth schrieb:
Lern lesen und zitieren, Junge.
Lern vielleicht ein bisschen den Ball flachzuhalten. Wir sind hier nämlich nicht mehr 25.
Es geht um die Verbindung von Schönheitsempfinden und "attraktiv finden". Zitat vielleicht missglückt, aber das war der Hintergrund. Dass es darum geht, den Code auch für andere so klar wie möglich zu schreiben.
Arcoth schrieb:
Ich werde auch in dreißig Jahren sofort sehen, was
++second
da verloren hat, da bin ich mir verdammt sicherDu vielleicht. Und deine Kollegen?
-
minastaros schrieb:
Jedes Handbuch über guten Code sagt, dass
for
nicht nur eine Art von Schleife ist, sondern bereits deren Aufgabe beschreibt, nämlich über eine bestimmte Menge der Reihe nach zu iterieren. Allein durch dieses Schlüsselwort hat der Leser bereits mehr Information als bei einem simplenwhile
.Jo. Sehe ich in sonst feinem ein for, weiß ich, daß über die ganze Sequenz iteriert wird. Welche Sequenz ist meist eh aus dem Kontext klar. Da bereite ich mich schon auf den Kern der Schleife vor, während ich der Vollständigkeit halber noch Initialisierungsausdruck lese, und spätestens dann weiß ich, daß es die Schleife war, die ich meinte und steige in den Körper ein, während ich noch halbherzig überfliege, daß die Laufbedingung stimmt und den Weiterschaltungsausdruck, kann ja nur ++i oder p=p->next oder so sein, falls nicht, fällt es mir nachträglich auf, obwohl die Augen schon unten sind: "Da hat doch gerade was nicht gestimmt?!", dann schalte ich vom 5. Gang in den 1. Gang zurück und habe einen Getriebeschaden. Und der ganzen Funktion oder der ganzen Datei stehe ich ein Weilchen mit Mißtrauen gegenüber.