Das ist nicht das, wofür Range-Based ist (heh, ist das ein Wortspiel?)
jrok
2
Dies ist in STL-Containern nur möglich, wenn std::findeine andere Overkill-Funktion verwendet wird. Sie können Iteratoren nicht aus enthaltenen Elementen schließen. Warum nicht einen Iterator warten?
Eitan T
2
Aus zwei Gründen. Das erste ist alles, was ich (in diesem Fall) tun möchte, um zu sehen, ob ich am letzten Element bin oder nicht :) und das zweite ist, dass der Compiler eines verwalten muss. Warum kann ich nicht darauf zugreifen? "this" ist eine Variable, deren Gültigkeitsbereich vom Compiler verwaltet wird. Warum nicht hier? Oder stellen Sie eine alternative (aber dennoch praktische) Syntax bereit, die wie Javascript eine Variable einrichtet, die sich ändert, wenn Sie die Schleife durchlaufen. für (Auto & Index: Liste)
Fred Finkle
1
@FredFinkle Sie haben tatsächlich Recht, es gibt einen Iterator , aber wenn Sie eine bereichsbasierte forSchleife verwenden, handelt es sich um einen compilerinternen Namen, der daher nicht in Ihrem Code verwendet werden kann. Wenn Sie also wirklich wissen möchten, ob Sie sich am letzten Element befinden, sollten Sie die for(;;)Schleife verwenden.
iFreilicht
Siehe auch
Antworten:
62
Ja du kannst, es braucht nur ein bisschen Massage;)
Der Trick besteht darin, die Komposition zu verwenden: Anstatt den Container direkt zu durchlaufen, "komprimieren" Sie ihn mit einem Index auf dem Weg.
Sie können es auf ideone sehen , obwohl es die Unterstützung für for-range-Schleifen fehlt, so dass es weniger hübsch ist.
BEARBEITEN:
Ich habe gerade daran gedacht, dass ich Boost.Range öfter überprüfen sollte. Leider keine zipReichweite, aber ich habe einen Perl gefunden : boost::adaptors::indexed. Es erfordert jedoch Zugriff auf den Iterator, um den Index abzurufen. Schade: x
Ansonsten mit dem counting_rangeund einem Generikum zipbin ich sicher, dass es möglich sein könnte, etwas Interessantes zu tun ...
Durch das zipautomatische Erstellen einer Ansicht als eine Reihe von Referenztupeln und das iota(0)einfache Erstellen eines "falschen" Bereichs, der von der 0Unendlichkeit (oder dem Maximum seines Typs ...) ausgeht und nur bis unendlich zählt.
Dies bietet eine Fülle nützlicher Informationen. Vielen Dank. Ich werde mit dem Code herumspielen. Wie oben erwähnt, ist der "Index" -Code genau das, was die Sprache bereitstellen soll.
@ildjarn: Ja, Boost.Iterators hat die Bausteine (wie es scheint), aber es gibt keinen entsprechenden Bereich, was ärgerlich ist.
Matthieu M.
Beachten Sie, dass Sie möglicherweise ändern Indexerkönnen, um rvalue-Argumente auch ordnungsgemäß zu akzeptieren und zu halten, indem Sie den Typ von _containerin einen Werttyp ändern, wenn das ursprüngliche Argument ein rvalue ist und std::move/ oder std::forwarddas Argument in.
Xeo
Ich habe den Code so bearbeitet, dass er hoffentlich mit rvalues funktioniert. Leider kann ich den Code nicht vollständig testen, aber er sollte funktionieren. Eine Erklärung finden Sie hier . Wenn es Ihnen nicht gefällt oder Sie Fehler oder ähnliches finden, können Sie jederzeit einen Rollback durchführen. :)
Xeo
30
jrok ist richtig: bereichsbasierte for-Schleifen sind nicht für diesen Zweck ausgelegt.
In Ihrem Fall ist es jedoch möglich, es mithilfe der Zeigerarithmetik zu berechnen, da vectorseine Elemente zusammenhängend gespeichert werden (*).
vector<int> list;
for(auto& elem:list) {
int i = elem;
int pos = &elem-&list[0]; // pos contains the position in the vector // also a &-operator overload proof alternative (thanks to ildjarn) :// int pos = addressof(elem)-addressof(list[0]);
}
Dies ist jedoch eindeutig eine schlechte Vorgehensweise, da der Code dadurch verschleiert und anfälliger wird (er kann leicht beschädigt werden, wenn jemand den Containertyp ändert, den &Operator überlastet oder "auto &" durch "auto" ersetzt. Viel Glück beim Debuggen!).
HINWEIS: Die Kontiguität ist für Vektoren in C ++ 03 und für Arrays und Zeichenfolgen in C ++ 11-Standard garantiert.
Ja, es ist in der Norm angegeben. Die Kontiguität ist vectorin C ++ 03 arrayund stringin C ++ 11 garantiert .
Nicol Bolas
1
„ Es leicht bricht , wenn jemand ... Überlastung der &Betreiber “ Das ist , was std::addressoffür ist. : -]
ildjarn
Du hast recht. Die & -Überlastungssichere Version wäre also: int pos = addressof (elem) - addressof (list [0]); .... Matthieu Ms Iterator Wrapper ist viel besser :)
Frédéric Terrazzoni
Wusste nicht, dass die Nähe garantiert ist. Ich würde es hier nicht benutzen wollen, aber gut zu wissen.
Fred Finkle
5
Warum nicht std :: distance verwenden, um die Position herauszufinden?
Michael van der Westhuizen
19
Nein, das kannst du nicht (zumindest nicht ohne Anstrengung). Wenn Sie die Position eines Elements benötigen, sollten Sie nicht bereichsbasiert für verwenden. Denken Sie daran, dass dies nur ein praktisches Tool für den häufigsten Fall ist: Führen Sie für jedes Element einen Code aus. In den selteneren Fällen, in denen Sie die Position des Elements benötigen, müssen Sie die weniger bequeme reguläre forSchleife verwenden.
Dies funktioniert ziemlich ähnlich wie die erwähnte "ideale Weltlösung", hat eine hübsche Syntax und ist prägnant. Beachten Sie, dass der Typ elin diesem Fall ungefähr so ist boost::foobar<const std::string&, int>, dass er die Referenz dort verarbeitet und kein Kopieren durchgeführt wird. Es ist sogar unglaublich effizient: https://godbolt.org/g/e4LMnJ (Der Code entspricht der Beibehaltung einer eigenen Zählervariablen, die so gut wie möglich ist)
Der Vollständigkeit halber die Alternativen:
size_t i = 0;
for(autoconst& el: strings) {
std::cout << i << ": " << el << std::endl;
++i;
}
Oder verwenden Sie die zusammenhängende Eigenschaft eines Vektors:
for(autoconst& el: strings) {
size_t i = &el - &strings.front();
std::cout << i << ": " << el << std::endl;
}
Der erste generiert den gleichen Code wie die Boost-Adapter-Version (optimal) und der letzte ist 1 Anweisung länger: https://godbolt.org/g/nEG8f9
Hinweis: Wenn Sie nur wissen möchten, ob Sie das letzte Element haben, können Sie Folgendes verwenden:
Dies funktioniert für jeden Standard - Container aber auto&/ auto const&muss verwendet werden (wie oben) , aber das ist auf jeden Fall zu empfehlen. Abhängig von der Eingabe kann dies auch ziemlich schnell sein (insbesondere wenn der Compiler die Größe Ihres Vektors kennt).
Ersetzen Sie das &fooby std::addressof(foo), um auf der sicheren Seite für generischen Code zu sein.
Ich habe die 2 Alternativen mit Godbolt-Vergleich des generierten Codes der Vollständigkeit halber hinzugefügt und auch die Notwendigkeit des OP (in den Kommentaren) zur Erkennung des letzten Elements
angesprochen
10
Wenn Sie einen Compiler mit C ++ 14-Unterstützung haben, können Sie dies in einem funktionalen Stil tun:
#include<iostream>#include<string>#include<vector>#include<functional>template<typename T>
voidfor_enum(T& container, std::function<void(int, typename T::value_type&)> op){
int idx = 0;
for(auto& value : container)
op(idx++, value);
}
intmain(){
std::vector<std::string> sv {"hi", "there"};
for_enum(sv, [](auto i, auto v) {
std::cout << i << " " << v << std::endl;
});
}
Funktioniert mit clang 3.4 und gcc 4.9 (nicht mit 4.8); für beide muss eingestellt werden -std=c++1y. Der Grund, warum Sie c ++ 14 benötigen, sind die autoParameter in der Lambda-Funktion.
std::functionverwendet Typ Löschung, die teuer ist. Warum nicht verwenden, template<typename T, typename Callable> void for_enum(T& container, Callable op)damit Sie nicht für das Löschen des Typs bezahlen müssen?
NathanOliver
5
Es gibt einen überraschend einfachen Weg, dies zu tun
vector<int> list;
for(auto& elem:list) {
int i = (&elem-&*(list.begin()));
}
Wenn Sie darauf bestehen, einen auf dem Index basierenden Bereich zu verwenden und den Index zu kennen, ist es ziemlich trivial, den Index wie unten gezeigt beizubehalten. Ich glaube nicht, dass es eine sauberere / einfachere Lösung für die Reichweite von Schleifen gibt. Aber warum nicht einen Standard für (;;) verwenden? Das würde wahrscheinlich Ihre Absicht und Ihren Code am klarsten machen.
vector<int> list;
int idx = 0;
for(auto& elem:list) {
int i = elem;
//TODO whatever made you want the idx
++idx;
}
(idx entspricht "Verwalten eines separaten Iterators")
user66081
2
Ich habe aus Ihren Kommentaren gelesen, dass ein Grund, warum Sie den Index kennen wollen, darin besteht, zu wissen, ob das Element das erste / letzte in der Sequenz ist. Wenn ja, können Sie tun
for(auto& elem:list) {
// loop code ...if(&elem == &*std::begin(list)){ ... special code for first element ... }
if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... }
// if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...}// loop code ...
}
BEARBEITEN: Hiermit wird beispielsweise ein Container gedruckt, der ein Trennzeichen im letzten Element überspringt. Funktioniert für die meisten Container, die ich mir vorstellen kann (einschließlich Arrays) (Online-Demo http://coliru.stacked-crooked.com/a/9bdce059abd87f91 ):
#include<iostream>#include<vector>#include<list>#include<set>usingnamespacestd;
template<class Container>
voidprint(Container const& c){
for(auto& x:c){
std::cout << x;
if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element
}
std::cout << std::endl;
}
intmain(){
std::vector<double> v{1.,2.,3.};
print(v); // prints 1,2,3std::list<double> l{1.,2.,3.};
print(l); // prints 1,2,3std::initializer_list<double> i{1.,2.,3.};
print(i); // prints 1,2,3std::set<double> s{1.,2.,3.};
print(s); // print 1,2,3double a[3] = {1.,2.,3.}; // works for C-arrays as well
print(a); // print 1,2,3
}
Bitte beachten Sie (vor ungerechtfertigtem Downvoting), dass der Autor der Frage dies im Zusammenhang mit der Erkennung des letzten Elements in einer for-range-Schleife für einen Container stellt. Dafür sehe ich keinen Grund, warum zu vergleichen &elemund &*std::prev(std::end(list))wird nicht funktionieren oder praktisch sein. Ich stimme der anderen Antwort zu, dass ein iteratorbasiertes for dafür besser geeignet ist, aber immer noch.
alfC
Es scheint einfacher zu sein, int i=c.size();vor der Schleife zu deklarieren und zu testen if(--i==0).
Marc Glisse
@MarcGlisse, der int iCode war nur ein Beispiel. Ich werde es entfernen, um Verwirrung zu vermeiden. Auch wenn Sie sizevor der Schleife verwenden, benötigen Sie einen Zähler.
AlfC
2
Tobias Widlund hat einen schönen MIT-lizenzierten Python-Header geschrieben, der nur aufgezählt wird (allerdings C ++ 17):
Hier ist eine makrobasierte Lösung, die wahrscheinlich die meisten anderen in Bezug auf Einfachheit, Kompilierungszeit und Qualität der Codegenerierung übertrifft:
#include<iostream>#define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true)intmain(){
fori(i, autoconst & x : {"hello", "world", "!"}) {
std::cout << i << " " << x << std::endl;
}
}
Wenn Sie vermeiden möchten, dass eine Hilfsfunktion geschrieben werden muss, während sich die Indexvariable lokal in der Schleife befindet, können Sie ein Lambda mit einer veränderlichen Variablen verwenden:
Die hier verwendeten Hauptfunktionen sind C ++ 20-Bereiche, C ++ 20-Konzepte, C ++ 11-veränderbare Lambdas, C ++ 14-Lambda-Capture-Initialisierer und C ++ 17-strukturierte Bindungen. Informationen zu diesen Themen finden Sie unter cppreference.com.
Beachten Sie, dass es sich elementbei der strukturierten Bindung tatsächlich um eine Referenz und nicht um eine Kopie des Elements handelt (nicht, dass es hier darauf ankommt). Dies liegt daran, dass alle Qualifizierer in der Umgebung autonur ein temporäres Objekt betreffen, aus dem die Felder extrahiert werden, und nicht die Felder selbst.
Der generierte Code ist identisch mit dem von diesem generierten Code (zumindest von gcc 10.2):
#include<array>#include<iostream>#include<ranges>automain() -> int{
autoconst elements = std::array{3, 1, 4, 1, 5, 9, 2};
for (auto index = std::size_t{}; auto& element : elements) {
std::cout << "Element " << index << ": " << element << '\n';
index++;
}
}
std::find
eine andere Overkill-Funktion verwendet wird. Sie können Iteratoren nicht aus enthaltenen Elementen schließen. Warum nicht einen Iterator warten?for
Schleife verwenden, handelt es sich um einen compilerinternen Namen, der daher nicht in Ihrem Code verwendet werden kann. Wenn Sie also wirklich wissen möchten, ob Sie sich am letzten Element befinden, sollten Sie diefor(;;)
Schleife verwenden.Antworten:
Ja du kannst, es braucht nur ein bisschen Massage;)
Der Trick besteht darin, die Komposition zu verwenden: Anstatt den Container direkt zu durchlaufen, "komprimieren" Sie ihn mit einem Index auf dem Weg.
Spezialisierte Postleitzahl:
template <typename T> struct iterator_extractor { typedef typename T::iterator type; }; template <typename T> struct iterator_extractor<T const> { typedef typename T::const_iterator type; }; template <typename T> class Indexer { public: class iterator { typedef typename iterator_extractor<T>::type inner_iterator; typedef typename std::iterator_traits<inner_iterator>::reference inner_reference; public: typedef std::pair<size_t, inner_reference> reference; iterator(inner_iterator it): _pos(0), _it(it) {} reference operator*() const { return reference(_pos, *_it); } iterator& operator++() { ++_pos; ++_it; return *this; } iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } bool operator==(iterator const& it) const { return _it == it._it; } bool operator!=(iterator const& it) const { return !(*this == it); } private: size_t _pos; inner_iterator _it; }; Indexer(T& t): _container(t) {} iterator begin() const { return iterator(_container.begin()); } iterator end() const { return iterator(_container.end()); } private: T& _container; }; // class Indexer template <typename T> Indexer<T> index(T& t) { return Indexer<T>(t); }
Und damit:
#include <iostream> #include <iterator> #include <limits> #include <vector> // Zipper code here int main() { std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9}; for (auto p: index(v)) { std::cout << p.first << ": " << p.second << "\n"; } }
Sie können es auf ideone sehen , obwohl es die Unterstützung für for-range-Schleifen fehlt, so dass es weniger hübsch ist.
BEARBEITEN:
Ich habe gerade daran gedacht, dass ich Boost.Range öfter überprüfen sollte. Leider keine
zip
Reichweite, aber ich habe einen Perl gefunden :boost::adaptors::indexed
. Es erfordert jedoch Zugriff auf den Iterator, um den Index abzurufen. Schade: xAnsonsten mit dem
counting_range
und einem Generikumzip
bin ich sicher, dass es möglich sein könnte, etwas Interessantes zu tun ...In der idealen Welt würde ich mir vorstellen:
int main() { std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9}; for (auto tuple: zip(iota(0), v)) { std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n"; } }
Durch das
zip
automatische Erstellen einer Ansicht als eine Reihe von Referenztupeln und dasiota(0)
einfache Erstellen eines "falschen" Bereichs, der von der0
Unendlichkeit (oder dem Maximum seines Typs ...) ausgeht und nur bis unendlich zählt.quelle
counting_range
(oderboost::counting_iterator
) +boost::zip_iterator
?Indexer
können, um rvalue-Argumente auch ordnungsgemäß zu akzeptieren und zu halten, indem Sie den Typ von_container
in einen Werttyp ändern, wenn das ursprüngliche Argument ein rvalue ist undstd::move
/ oderstd::forward
das Argument in.jrok ist richtig: bereichsbasierte for-Schleifen sind nicht für diesen Zweck ausgelegt.
In Ihrem Fall ist es jedoch möglich, es mithilfe der Zeigerarithmetik zu berechnen, da
vector
seine Elemente zusammenhängend gespeichert werden (*).vector<int> list; for(auto& elem:list) { int i = elem; int pos = &elem-&list[0]; // pos contains the position in the vector // also a &-operator overload proof alternative (thanks to ildjarn) : // int pos = addressof(elem)-addressof(list[0]); }
Dies ist jedoch eindeutig eine schlechte Vorgehensweise, da der Code dadurch verschleiert und anfälliger wird (er kann leicht beschädigt werden, wenn jemand den Containertyp ändert, den
&
Operator überlastet oder "auto &" durch "auto" ersetzt. Viel Glück beim Debuggen!).HINWEIS: Die Kontiguität ist für Vektoren in C ++ 03 und für Arrays und Zeichenfolgen in C ++ 11-Standard garantiert.
quelle
vector
in C ++ 03array
undstring
in C ++ 11 garantiert .&
Betreiber “ Das ist , wasstd::addressof
für ist. : -]Nein, das kannst du nicht (zumindest nicht ohne Anstrengung). Wenn Sie die Position eines Elements benötigen, sollten Sie nicht bereichsbasiert für verwenden. Denken Sie daran, dass dies nur ein praktisches Tool für den häufigsten Fall ist: Führen Sie für jedes Element einen Code aus. In den selteneren Fällen, in denen Sie die Position des Elements benötigen, müssen Sie die weniger bequeme reguläre
for
Schleife verwenden.quelle
Basierend auf der Antwort von @Matthieu gibt es eine sehr elegante Lösung mit den genannten boost :: adapters :: indexed :
std::vector<std::string> strings{10, "Hello"}; int main(){ strings[5] = "World"; for(auto const& el: strings| boost::adaptors::indexed(0)) std::cout << el.index() << ": " << el.value() << std::endl; }
Du kannst es versuchen
Dies funktioniert ziemlich ähnlich wie die erwähnte "ideale Weltlösung", hat eine hübsche Syntax und ist prägnant. Beachten Sie, dass der Typ
el
in diesem Fall ungefähr so istboost::foobar<const std::string&, int>
, dass er die Referenz dort verarbeitet und kein Kopieren durchgeführt wird. Es ist sogar unglaublich effizient: https://godbolt.org/g/e4LMnJ (Der Code entspricht der Beibehaltung einer eigenen Zählervariablen, die so gut wie möglich ist)Der Vollständigkeit halber die Alternativen:
size_t i = 0; for(auto const& el: strings) { std::cout << i << ": " << el << std::endl; ++i; }
Oder verwenden Sie die zusammenhängende Eigenschaft eines Vektors:
for(auto const& el: strings) { size_t i = &el - &strings.front(); std::cout << i << ": " << el << std::endl; }
Der erste generiert den gleichen Code wie die Boost-Adapter-Version (optimal) und der letzte ist 1 Anweisung länger: https://godbolt.org/g/nEG8f9
Hinweis: Wenn Sie nur wissen möchten, ob Sie das letzte Element haben, können Sie Folgendes verwenden:
for(auto const& el: strings) { bool isLast = &el == &strings.back(); std::cout << isLast << ": " << el << std::endl; }
Dies funktioniert für jeden Standard - Container aber
auto&
/auto const&
muss verwendet werden (wie oben) , aber das ist auf jeden Fall zu empfehlen. Abhängig von der Eingabe kann dies auch ziemlich schnell sein (insbesondere wenn der Compiler die Größe Ihres Vektors kennt).Ersetzen Sie das
&foo
bystd::addressof(foo)
, um auf der sicheren Seite für generischen Code zu sein.quelle
Wenn Sie einen Compiler mit C ++ 14-Unterstützung haben, können Sie dies in einem funktionalen Stil tun:
#include <iostream> #include <string> #include <vector> #include <functional> template<typename T> void for_enum(T& container, std::function<void(int, typename T::value_type&)> op) { int idx = 0; for(auto& value : container) op(idx++, value); } int main() { std::vector<std::string> sv {"hi", "there"}; for_enum(sv, [](auto i, auto v) { std::cout << i << " " << v << std::endl; }); }
Funktioniert mit clang 3.4 und gcc 4.9 (nicht mit 4.8); für beide muss eingestellt werden
-std=c++1y
. Der Grund, warum Sie c ++ 14 benötigen, sind dieauto
Parameter in der Lambda-Funktion.quelle
std::function
verwendet Typ Löschung, die teuer ist. Warum nicht verwenden,template<typename T, typename Callable> void for_enum(T& container, Callable op)
damit Sie nicht für das Löschen des Typs bezahlen müssen?Es gibt einen überraschend einfachen Weg, dies zu tun
vector<int> list; for(auto& elem:list) { int i = (&elem-&*(list.begin())); }
Wo
i
wird Ihr erforderlicher Index sein?Dies nutzt die Tatsache aus, dass C ++ - Vektoren immer zusammenhängend sind .
quelle
Wenn Sie darauf bestehen, einen auf dem Index basierenden Bereich zu verwenden und den Index zu kennen, ist es ziemlich trivial, den Index wie unten gezeigt beizubehalten. Ich glaube nicht, dass es eine sauberere / einfachere Lösung für die Reichweite von Schleifen gibt. Aber warum nicht einen Standard für (;;) verwenden? Das würde wahrscheinlich Ihre Absicht und Ihren Code am klarsten machen.
vector<int> list; int idx = 0; for(auto& elem:list) { int i = elem; //TODO whatever made you want the idx ++idx; }
quelle
Ich habe aus Ihren Kommentaren gelesen, dass ein Grund, warum Sie den Index kennen wollen, darin besteht, zu wissen, ob das Element das erste / letzte in der Sequenz ist. Wenn ja, können Sie tun
for(auto& elem:list) { // loop code ... if(&elem == &*std::begin(list)){ ... special code for first element ... } if(&elem == &*std::prev(std::end(list))){ ... special code for last element ... } // if(&elem == &*std::rbegin(list)){... (C++14 only) special code for last element ...} // loop code ... }
BEARBEITEN: Hiermit wird beispielsweise ein Container gedruckt, der ein Trennzeichen im letzten Element überspringt. Funktioniert für die meisten Container, die ich mir vorstellen kann (einschließlich Arrays) (Online-Demo http://coliru.stacked-crooked.com/a/9bdce059abd87f91 ):
#include <iostream> #include <vector> #include <list> #include <set> using namespace std; template<class Container> void print(Container const& c){ for(auto& x:c){ std::cout << x; if(&x != &*std::prev(std::end(c))) std::cout << ", "; // special code for last element } std::cout << std::endl; } int main() { std::vector<double> v{1.,2.,3.}; print(v); // prints 1,2,3 std::list<double> l{1.,2.,3.}; print(l); // prints 1,2,3 std::initializer_list<double> i{1.,2.,3.}; print(i); // prints 1,2,3 std::set<double> s{1.,2.,3.}; print(s); // print 1,2,3 double a[3] = {1.,2.,3.}; // works for C-arrays as well print(a); // print 1,2,3 }
quelle
&elem
und&*std::prev(std::end(list))
wird nicht funktionieren oder praktisch sein. Ich stimme der anderen Antwort zu, dass ein iteratorbasiertes for dafür besser geeignet ist, aber immer noch.int i=c.size();
vor der Schleife zu deklarieren und zu testenif(--i==0)
.int i
Code war nur ein Beispiel. Ich werde es entfernen, um Verwirrung zu vermeiden. Auch wenn Siesize
vor der Schleife verwenden, benötigen Sie einen Zähler.Tobias Widlund hat einen schönen MIT-lizenzierten Python-Header geschrieben, der nur aufgezählt wird (allerdings C ++ 17):
GitHub
Blogeintrag
Wirklich schön zu bedienen:
std::vector<int> my_vector {1,3,3,7}; for(auto [i, my_element] : en::enumerate(my_vector)) { // do stuff }
quelle
Hier ist eine makrobasierte Lösung, die wahrscheinlich die meisten anderen in Bezug auf Einfachheit, Kompilierungszeit und Qualität der Codegenerierung übertrifft:
#include <iostream> #define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true) int main() { fori(i, auto const & x : {"hello", "world", "!"}) { std::cout << i << " " << x << std::endl; } }
Ergebnis:
$ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate 0 hello 1 world 2 !
quelle
Wenn Sie vermeiden möchten, dass eine Hilfsfunktion geschrieben werden muss, während sich die Indexvariable lokal in der Schleife befindet, können Sie ein Lambda mit einer veränderlichen Variablen verwenden:
int main() { std::vector<char> values = {'a', 'b', 'c'}; std::for_each(begin(values), end(values), [i = size_t{}] (auto x) mutable { std::cout << i << ' ' << x << '\n'; ++i; }); }
quelle
Hier ist eine sehr schöne Lösung mit c ++ 20:
#include <array> #include <iostream> #include <ranges> template<typename T> struct EnumeratedElement { std::size_t index; T& element; }; auto enumerate(std::ranges::range auto& range) -> std::ranges::view auto { return range | std::views::transform( [i = std::size_t{}](auto& element) mutable { return EnumeratedElement{i++, element}; } ); } auto main() -> int { auto const elements = std::array{3, 1, 4, 1, 5, 9, 2}; for (auto const [index, element] : enumerate(elements)) { std::cout << "Element " << index << ": " << element << '\n'; } }
Die hier verwendeten Hauptfunktionen sind C ++ 20-Bereiche, C ++ 20-Konzepte, C ++ 11-veränderbare Lambdas, C ++ 14-Lambda-Capture-Initialisierer und C ++ 17-strukturierte Bindungen. Informationen zu diesen Themen finden Sie unter cppreference.com.
Beachten Sie, dass es sich
element
bei der strukturierten Bindung tatsächlich um eine Referenz und nicht um eine Kopie des Elements handelt (nicht, dass es hier darauf ankommt). Dies liegt daran, dass alle Qualifizierer in der Umgebungauto
nur ein temporäres Objekt betreffen, aus dem die Felder extrahiert werden, und nicht die Felder selbst.Der generierte Code ist identisch mit dem von diesem generierten Code (zumindest von gcc 10.2):
#include <array> #include <iostream> #include <ranges> auto main() -> int { auto const elements = std::array{3, 1, 4, 1, 5, 9, 2}; for (auto index = std::size_t{}; auto& element : elements) { std::cout << "Element " << index << ": " << element << '\n'; index++; } }
Beweis: https://godbolt.org/z/a5bfxz
quelle