Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden.
Du solltest ein Upgrade durchführen oder einen alternativen Browser verwenden.
Du solltest ein Upgrade durchführen oder einen alternativen Browser verwenden.
C++ kapiere 'range based for loops' nicht
- Ersteller lemon03
- Erstellt am
DaysShadow
Admiral
- Registriert
- Jan. 2009
- Beiträge
- 8.834
Weil das in C++ nicht funktioniert?
Klassisch wäre
In neueren C++ Versionen gibt's foreach Schleifen, die gehen denke ich auch über int arrays bspw., wenn die on the fly erstellt werden können müsste das so ähnlich möglich sein wie du es willst.
Da kenne ich mich aber im aktuellen C++ nicht weiter aus und du musst selbst nachschauen.
Python kann das was du willst bspw. ganz einfach mit
Klassisch wäre
Code:
for( int i = 0; i < 100; i++ )
{
// Do stuff.
}
In neueren C++ Versionen gibt's foreach Schleifen, die gehen denke ich auch über int arrays bspw., wenn die on the fly erstellt werden können müsste das so ähnlich möglich sein wie du es willst.
Da kenne ich mich aber im aktuellen C++ nicht weiter aus und du musst selbst nachschauen.
Python kann das was du willst bspw. ganz einfach mit
Code:
for i in range(0,100):
#do stuff
violentviper
Lt. Commander
- Registriert
- Mai 2008
- Beiträge
- 1.661
Dann deklariere doch mal ein Anfang und ein Ende?
Yuuri
Fleet Admiral
- Registriert
- Okt. 2010
- Beiträge
- 13.923
Du hast keine geeignete Struktur zum Drüberiterieren.
http://en.cppreference.com/w/cpp/language/range-for
http://en.cppreference.com/w/cpp/language/range-for
Code:
for (auto&& [first,second] : mymap) {
// use first and second
}
- Registriert
- Juli 2004
- Beiträge
- 550
Diese for-loops wurden ab dem C++11 standard eingeführt, und ich wollte sie eben benutzen um das klassische 'von 0 bis 100' zu ersetzen. Selbst habe ich sie schon eingesetzt, zB
(Sorry für das jetzt nicht nachvollziehbare Beispiel, kapiere nur eben nicht, warum analog mein Eingangsbeispiel nicht funktioniert)
In der Referenz stehen folgende Beispiele:
Wenn man jetzt statt den vector einfach einen int=100 nimmt, kapiere ich nicht, warum mein Beispiel nicht funktioniert?
@ Yuuri
Und das kapiere ich eben nicht.
Code:
for (auto j : char_col)
{
for (auto i : char_form)
{
ch.Char.UnicodeChar = i;
ch.Attributes = j;
palette_chars.push_back(ch);
}
}
In der Referenz stehen folgende Beispiele:
Code:
int main() {
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (const int& i : v) // access by const reference
std::cout << i << ' ';
std::cout << '\n';
for (auto i : v) // access by value, the type of i is int
std::cout << i << ' ';
std::cout << '\n';
for (auto&& i : v) // access by reference, the type of i is int&
std::cout << i << ' ';
std::cout << '\n';
for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a braced-init-list
std::cout << n << ' ';
std::cout << '\n';
int a[] = {0, 1, 2, 3, 4, 5};
for (int n : a) // the initializer may be an array
std::cout << n << ' ';
std::cout << '\n';
for (int n : a)
std::cout << 1 << ' '; // the loop variable need not be used
std::cout << '\n';
}
@ Yuuri
Und das kapiere ich eben nicht.
rg88
Fleet Admiral
- Registriert
- Feb. 2015
- Beiträge
- 34.698
Alle diese Beispiele beziehen sich auf Arrays bzw. eine Abwandlung davon.
Du willst aber eine Zählschleife. 100 ist nunmal kein array.
Dein Denkfehler ist, dass du eine Funktionalität nutzt die du falsch verstehst. Das würde funktionieren wenn 100 eine array mit 0;1;2... usw wäre. Ist es aber nicht. Also kannst du dir da keine einzelnen Werte rauspicken wie 1, 2 usw.
Du willst aber eine Zählschleife. 100 ist nunmal kein array.
Dein Denkfehler ist, dass du eine Funktionalität nutzt die du falsch verstehst. Das würde funktionieren wenn 100 eine array mit 0;1;2... usw wäre. Ist es aber nicht. Also kannst du dir da keine einzelnen Werte rauspicken wie 1, 2 usw.
- Registriert
- Juli 2004
- Beiträge
- 550
Mmh, ich glaube, so dämmerts mir. Würde ich also statt der 100 ein int array/vector mit hundert Einträgen habe, würde es so gehen?
EDIT: stimmt, so geht es dann
EDIT2: aber so stehts ja schon oben
Gibt es dann noch eine Möglichkeit, Ungetüme wie 'for (int i = 0; i < 100; ++ i)' kürzer zu fassen?
EDIT: stimmt, so geht es dann
Code:
std::array <int, 4> arr = { 0, 1, 2, 3 }; //bitte bis 100 ausfüllen...
for (int i : arr) std::cout << i << '\n';
Gibt es dann noch eine Möglichkeit, Ungetüme wie 'for (int i = 0; i < 100; ++ i)' kürzer zu fassen?
Zuletzt bearbeitet:
- Registriert
- Juli 2004
- Beiträge
- 550
War nur sone Idee, 'for (int i : 100)' erschien mir auf den ersten Blick so schön einfach. Gibts ja so Leute, die zB für eine Endlosschleife
schreiben, dachte mir kann jemand einen Tipp geben, wie man das für einfache Zählschleifen schreibt.
@ vander
dann wüsste ich gerne, warum in einigen Foren
verpönt ist, bzw warum man sich darüber lustig macht? Ist ja wohl lesbarer als das genannte
?
Code:
for (;;)
Ergänzung ()
@ vander
dann wüsste ich gerne, warum in einigen Foren
Code:
while (true)
Code:
for (;;)
Zuletzt bearbeitet:
(code tags wg smiley umwandlung)
Kanibal
Lt. Commander
- Registriert
- Dez. 2011
- Beiträge
- 1.032
Du könntest Dir eine eigene Klasse bauen, die entsprechende Iterators anbietet... ungefähr so:
Code:
#include <algorithm>
#include <iostream>
namespace util {
class range {
public:
class iterator {
friend class range;
public:
long int operator *() const { return i_; }
const iterator &operator ++() { ++i_; return *this; }
iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }
bool operator ==(const iterator &other) const { return i_ == other.i_; }
bool operator !=(const iterator &other) const { return i_ != other.i_; }
protected:
iterator(long int start) : i_ (start) { }
private:
unsigned long i_;
};
iterator begin() const { return begin_; }
iterator end() const { return end_; }
range(long int begin, long int end) : begin_(begin), end_(end) {}
private:
iterator begin_;
iterator end_;
};
}
int main(int argc, char* argv[]) {
auto range = util::range(0, 100);
for(auto i : range) {
std::cout << i << std::endl;
}
return 0;
}
kuddlmuddl
Commander
- Registriert
- Mai 2010
- Beiträge
- 2.672
Überleg mal: Was sind denn die Werte, welche von i durchlaufen werden bei for (int i : 100)? 0 bis 100? Oder 1 bis 100?
Das ist nicht eindeutig, weil 100 quasi nur das Ende aber nicht den Start eindeutig vorgibt.
Das ist nicht eindeutig, weil 100 quasi nur das Ende aber nicht den Start eindeutig vorgibt.
Finalspace
Lt. Junior Grade
- Registriert
- Sep. 2009
- Beiträge
- 334
Kanibal schrieb:Du könntest Dir eine eigene Klasse bauen, die entsprechende Iterators anbietet...
Was zur Hölle?
Hast du dir mal den resultierenden Disassembly-Code angeschaut?
Hier merkt man sehr schön, wieviel unnütze CPU-Instruktionen verschwendet werden - nur um ne Schleifenoperation zu Abstrahieren!
Ganz zu schweigen das die API falsch ist, "end" sollte auch wirklich end sein und nicht end - 1 -.-
Normale for-loop (x64, Debug):
Code:
for (int i = firstIndex; i <= lastIndex; ++i) {
000000013F4424DE mov eax,dword ptr [firstIndex]
000000013F4424E1 mov dword ptr [rbp+10C4h],eax
000000013F4424E7 jmp main+0F7h (013F4424F7h)
000000013F4424E9 mov eax,dword ptr [rbp+10C4h]
000000013F4424EF inc eax
000000013F4424F1 mov dword ptr [rbp+10C4h],eax
000000013F4424F7 mov eax,dword ptr [lastIndex]
000000013F4424FA cmp dword ptr [rbp+10C4h],eax
000000013F442500 jg main+127h (013F442527h)
std::cout << i << std::endl;
000000013F442502 mov edx,dword ptr [rbp+10C4h]
000000013F442508 mov rcx,qword ptr [__imp_std::cout (013F451150h)]
000000013F44250F call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F451170h)]
000000013F442515 lea rdx,[std::endl<char,std::char_traits<char> > (013F4410B9h)]
000000013F44251C mov rcx,rax
000000013F44251F call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F451178h)]
}
Auto range Ding (x64, Debug):
Code:
auto range = util::range(firstIndex, lastIndex + 1);
000000013F96282E mov eax,dword ptr [lastIndex]
000000013F962831 inc eax
000000013F962833 mov r8d,eax
000000013F962836 mov edx,dword ptr [firstIndex]
000000013F962839 lea rcx,[range]
000000013F962840 call util::range::range (013F961235h)
for (auto i : range) {
000000013F962845 lea rax,[range]
000000013F96284C mov qword ptr [rbp+10E8h],rax
000000013F962853 lea rdx,[rbp+1104h]
000000013F96285A mov rcx,qword ptr [rbp+10E8h]
000000013F962861 call util::range::begin (013F9612BCh)
000000013F962866 lea rdx,[rbp+1124h]
000000013F96286D mov rcx,qword ptr [rbp+10E8h]
000000013F962874 call util::range::end (013F9611C2h)
000000013F962879 jmp main+137h (013F962887h)
000000013F96287B lea rcx,[rbp+1104h]
000000013F962882 call util::range::iterator::operator++ (013F961415h)
000000013F962887 lea rdx,[rbp+1124h]
000000013F96288E lea rcx,[rbp+1104h]
000000013F962895 call util::range::iterator::operator!= (013F9613A2h)
000000013F96289A movzx eax,al
000000013F96289D test eax,eax
000000013F96289F je main+188h (013F9628D8h)
000000013F9628A1 lea rcx,[rbp+1104h]
000000013F9628A8 call util::range::iterator::operator* (013F961271h)
000000013F9628AD mov dword ptr [rbp+1144h],eax
std::cout << i << std::endl;
000000013F9628B3 mov edx,dword ptr [rbp+1144h]
000000013F9628B9 mov rcx,qword ptr [__imp_std::cout (013F971150h)]
000000013F9628C0 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F971170h)]
000000013F9628C6 lea rdx,[std::endl<char,std::char_traits<char> > (013F9610B9h)]
000000013F9628CD mov rcx,rax
000000013F9628D0 call qword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (013F971178h)]
}
000000013F9628D6 jmp main+12Bh (013F96287Bh)
Der Code:
Code:
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <stdint.h>
namespace util {
class range {
public:
class iterator {
friend class range;
public:
long int operator *() const {
return i_;
}
const iterator &operator ++() {
++i_; return *this;
}
iterator operator ++(int) {
iterator copy(*this); ++i_; return copy;
}
bool operator ==(const iterator &other) const {
return i_ == other.i_;
}
bool operator !=(const iterator &other) const {
return i_ != other.i_;
}
protected:
iterator(long int start) : i_(start) {
}
private:
unsigned long i_;
};
iterator begin() const {
return begin_;
}
iterator end() const {
return end_;
}
range(long int begin, long int end) : begin_(begin), end_(end) {
}
private:
iterator begin_;
iterator end_;
};
};
int main(int argc, char *args[]) {
int firstIndex = 0;
int lastIndex = 100;
// Make sure that we push useless stuff into the cacheline: Not sure if this will work...
int arr[1024];
for (int i = 0; i < 1024; ++i) {
arr[i] = rand();
}
for (int i = 0; i < 1024; ++i) {
arr[i] /= 2;
}
uint64_t startCycles = __rdtsc();
#if 1
auto range = util::range(firstIndex, lastIndex + 1);
for (auto i : range) {
std::cout << i << std::endl;
}
#else
for (int i = firstIndex; i <= lastIndex; ++i) {
std::cout << i << std::endl;
}
#endif
uint64_t endCycles = __rdtsc();
uint64_t deltaCycles = endCycles - startCycles;
std::cout << "Delta cycles: " << deltaCycles << std::endl;
return 0;
}
Man kann auch mit nem Presslufthammer nen Nagel in die Wand schlagen.
Wenn man die CPU-Zyklen sich anschaut dann haben wir ne ungefähre Differenz von:
77752550 vs 86202203
Das ist ein Unterschied von 8449653 Zyklen!!!
Damit kann ich 281655 Wurzelberechnungen (30 Zyklen pro Squareroot) oder 2 Mio. Multiplikationen (4 Zyklen pro mul) ausführen.
Das ist Grund warum Software und das Web heutzutage einfach nur unglaublich langsam ist - wegen solch einem Abstraktionswahnsinn.
Zuletzt bearbeitet:
bog
Ensign
- Registriert
- Apr. 2008
- Beiträge
- 254
lemon03 schrieb:dann wüsste ich gerne, warum in einigen Foren
verpönt ist, bzw warum man sich darüber lustig macht? Ist ja wohl lesbarer als das genannteCode:while (true)
?Code:for (;;)
als ich die frage gestellt habe wurde mir gesagt, das laege an der laenge der ausdruecke (ersteres hat ohne leerzeichen 11, zweiteres nur 7 zeichen). so grob unklar, was for(;; ) macht, ist das jetzt beim lesen auch nicht...
VikingGe
Lt. Junior Grade
- Registriert
- Feb. 2017
- Beiträge
- 369
@Finalspace welcher Compiler? Mit oder ohne Optimierungen?
Aufgrund der ganzen unsinnigen Calls in deinem ASM-Code tippe ich mal auf ohne Optimierungen. Jeder halbwegs brauchbare Compiler sollte das aber problemlos wegoptimieren, bei Clang 4.0 kommt spätestens ab -O2 identischer Code bei beiden Varianten raus.
Ein Range-Iterator kann durchaus sinnvoll sein, wenn man ohnehin schon Funktionen herumfliegen hat, die Iteratoren akzeptieren (der Iterator-Typ ist dann eben ein Template-Parameter jener Funktionen). Solange da keine virtuellen Funktionen aufgerufen werden, wie etwa bei Java, ist das eine Zero Cost-Abstraktion.
Gleiches gilt nebenbei auch für Dinge wie Range-Checks bei Array-Zugriffen. Solange der Compiler irgendwie wissen kann, dass ein Index kleiner ist als die Länge des Arrays, wird auch der Range-Check wegoptimiert.
Aufgrund der ganzen unsinnigen Calls in deinem ASM-Code tippe ich mal auf ohne Optimierungen. Jeder halbwegs brauchbare Compiler sollte das aber problemlos wegoptimieren, bei Clang 4.0 kommt spätestens ab -O2 identischer Code bei beiden Varianten raus.
Ein Range-Iterator kann durchaus sinnvoll sein, wenn man ohnehin schon Funktionen herumfliegen hat, die Iteratoren akzeptieren (der Iterator-Typ ist dann eben ein Template-Parameter jener Funktionen). Solange da keine virtuellen Funktionen aufgerufen werden, wie etwa bei Java, ist das eine Zero Cost-Abstraktion.
Gleiches gilt nebenbei auch für Dinge wie Range-Checks bei Array-Zugriffen. Solange der Compiler irgendwie wissen kann, dass ein Index kleiner ist als die Länge des Arrays, wird auch der Range-Check wegoptimiert.
Wusste gar nicht, dass C++ auch eine Syntax hat, um Werte-Paare zu zerlegen, wieder was gelernt.Yuuri schrieb:Code:for (auto&& [first,second] : mymap) [...]
Zuletzt bearbeitet:
Kanibal
Lt. Commander
- Registriert
- Dez. 2011
- Beiträge
- 1.032
@Finalspace
Du müsstest noch Compilerversion und Flags angeben, um mit den "Benchmarks" irgendeine Aussagekraft generieren zu können.
Nur so viel: Schau Dir mal das Disassembly von 'clang4.0.0' mit '-O3 -std=c++14' an.
Grundsätzlich stimme ich Dir aber zu: es macht oftmals wenig Sinn, eine klassische for-Schleife durch Iterators zu ersetzen.
Du müsstest noch Compilerversion und Flags angeben, um mit den "Benchmarks" irgendeine Aussagekraft generieren zu können.
Nur so viel: Schau Dir mal das Disassembly von 'clang4.0.0' mit '-O3 -std=c++14' an.
Grundsätzlich stimme ich Dir aber zu: es macht oftmals wenig Sinn, eine klassische for-Schleife durch Iterators zu ersetzen.
@Finalspace: Selten so einen sinnlosen Benchmark gesehen.
Man kanns übrigens auch noch etwas minimalistischer gestalten:
Das ganze kann man noch mit templates für verschiedene Integertypen generalisieren, aber ich wollte es einfach halten.
Viel weniger Tipparbeit ist
dann allerdings nicht nicht wirklich
Man kanns übrigens auch noch etwas minimalistischer gestalten:
Code:
namespace detail {
struct IIterator {
int _v;
int operator*() const {
return _v;
}
void operator++() {
++_v;
}
friend bool operator!=(IIterator l, IIterator r) {
return l._v != r._v;
}
};
struct Irange_t {
int _begin;
int _end;
IIterator begin() const { return { _begint }; }
IIterator end() const { return { _end }; }
};
}
detail::Irange_t irange(int begin, int end) {
return { begin, end };
}
detail::Irange_t irange(int end) {
return { 0, end };
}
int main() {
for (auto i : irange(5, 20)) {
std::cout << i << std::endl;
}
for (auto i : irange(20)) {
std::cout << i << std::endl;
}
}
Viel weniger Tipparbeit ist
Code:
for (auto i : irange(20))
Finalspace
Lt. Junior Grade
- Registriert
- Sep. 2009
- Beiträge
- 334
Ist mit MSVC++ kompiliert, unter Debug-Mode:
/GS /W3 /Zc:wchar_t /ZI /Gm /Od /Fd"x64\Debug\vc140.pdb" /Zc:inline /fprecise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReportrompt /WX- /Zc:forScope /RTC1 /Gd /MDd /Fa"x64\Debug" /EHsc /nologo /Fo"x64\Debug" /Fp"x64\Debug\GrottigerCode.pch"
Ja mit Optimierungen ist die Range Loop schneller, aber immer noch langsamer als die normale Forschleife unter VC++! Selbst wenn CLANG das komplett herausoptimiert, dann ist das für mich dennoch untragbar - da man 90% der Zeit ohne Optimierungen entwickelt!
Ich kenn niemand der komplett immer Optimierungen (O2) an hat und dann problemlos alles Debuggen kann. Alleine schon das Code verschobene werden kann, macht es deutlich schwerer als unter Debug zu arbeiten. Das macht man eigentlich nur, wenn es beim Kunden als Release läuft und man nen Problem hat was nur unter Release auftritt.
Ich kann beim besten Willen nicht verstehen wie man sowas gegenüber normalen for-schleifen bevorzugen kann. Einfach nur Tippfaul und angst vorm programmieren -.-
Automatische Iteratoren sollen verwendet werden wenn es Sinn macht, z.b. wenn man die Anzahl nicht weiß (DB-Query). Von mir aus noch in STL Containern, wie z.b. std::map - aber bitte nicht bei normalen Aufzählungen.
Ich bin raus, das ist mir echt zuviel...
/GS /W3 /Zc:wchar_t /ZI /Gm /Od /Fd"x64\Debug\vc140.pdb" /Zc:inline /fprecise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReportrompt /WX- /Zc:forScope /RTC1 /Gd /MDd /Fa"x64\Debug" /EHsc /nologo /Fo"x64\Debug" /Fp"x64\Debug\GrottigerCode.pch"
Ja mit Optimierungen ist die Range Loop schneller, aber immer noch langsamer als die normale Forschleife unter VC++! Selbst wenn CLANG das komplett herausoptimiert, dann ist das für mich dennoch untragbar - da man 90% der Zeit ohne Optimierungen entwickelt!
Ich kenn niemand der komplett immer Optimierungen (O2) an hat und dann problemlos alles Debuggen kann. Alleine schon das Code verschobene werden kann, macht es deutlich schwerer als unter Debug zu arbeiten. Das macht man eigentlich nur, wenn es beim Kunden als Release läuft und man nen Problem hat was nur unter Release auftritt.
Ich kann beim besten Willen nicht verstehen wie man sowas gegenüber normalen for-schleifen bevorzugen kann. Einfach nur Tippfaul und angst vorm programmieren -.-
Automatische Iteratoren sollen verwendet werden wenn es Sinn macht, z.b. wenn man die Anzahl nicht weiß (DB-Query). Von mir aus noch in STL Containern, wie z.b. std::map - aber bitte nicht bei normalen Aufzählungen.
Ich bin raus, das ist mir echt zuviel...
Zuletzt bearbeitet:
Kanibal
Lt. Commander
- Registriert
- Dez. 2011
- Beiträge
- 1.032
Besser ist es. Du beschwerst Dich gerade über die 'Performance' eines Debug-Builds.Finalspace schrieb:Ich bin raus, das ist mir echt zuviel...
asdfman
Commander
- Registriert
- März 2008
- Beiträge
- 2.315
Kanibal schrieb:Besser ist es. Du beschwerst Dich gerade über die 'Performance' eines Debug-Builds.
Ey, Moment mal. Er programmiert seit 1995. 1995! Du hast Unrecht.
lemon03 schrieb:dann wüsste ich gerne, warum in einigen Foren
verpönt ist, bzw warum man sich darüber lustig macht? Ist ja wohl lesbarer als das genannteCode:while (true)
?Code:for (;;)
Einige Compiler werfen bei strengen Einstellungen eine Warning für "while(true)" raus (Conditional expression constant o.ä.), diese Warning gibts bei for(;; ) nicht. Ansonsten fällt mir kein wirklicher Grund ein. Mit solchen Details sollte man aber sowieso nicht allzuviel Zeit vergeuden... Mach es doch einfach so, wie es dir gefällt!
Auch interessant zu sehen, dass hier Assembly-Code von Debug-Builds verglichen wird!
Zuletzt bearbeitet:
Ähnliche Themen
- Antworten
- 2
- Aufrufe
- 560
- Antworten
- 11
- Aufrufe
- 1.149
- Antworten
- 11
- Aufrufe
- 796