it-swarm.com.de

Iteration über std :: vector: unsignierte vs. signierte Indexvariable

Wie kann ich einen Vektor in C++ iterieren?

Betrachten Sie diese beiden Codefragmente, dies funktioniert gut:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

und das hier:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

was warning: comparison between signed and unsigned integer expressions generiert.

Ich bin neu in der Welt von C++, daher sieht die Variable unsigned für mich ein wenig beängstigend aus und ich weiß, dass unsigned-Variablen gefährlich sein können, wenn sie nicht korrekt verwendet wird. Ist dies richtig?

423
Yuval Adam

Rückwärts iterieren

Siehe diese Antwort

Vorwärts iterieren

Das ist fast identisch. Ändern Sie einfach die Iteratoren/das Swap-Dekrement in Schritten. Sie sollten Iteratoren bevorzugen. Einige Leute empfehlen Ihnen, std::size_t als Indexvariablentyp zu verwenden. Das ist jedoch nicht portabel. Verwenden Sie immer den size_type-Typedef des Containers. (Während Sie im Fall der fortlaufenden Iteration nur mit einer Konvertierung davonkommen könnten, kann es im Fall der umgekehrten Iteration bei Verwendung von std::size_t tatsächlich ganz schief gehen, falls std::size_t breiter ist als der, was der typedef von size_type):

Mit std :: vector

Iteratoren verwenden

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

Wichtig ist, verwenden Sie immer das Präfix-Inkrement-Formular für Iteratoren, deren Definitionen Sie nicht kennen. Dadurch wird sichergestellt, dass Ihr Code so generisch wie möglich ausgeführt wird. 

Verwenden von Range C++ 11

for(auto const& value: a) {
     /* std::cout << value; ... */

Indices verwenden

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Arrays verwenden

Iteratoren verwenden

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Verwenden von Range C++ 11

for(auto const& value: a) {
     /* std::cout << value; ... */

Indices verwenden

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Lesen Sie in der Iterationsantwort zurück, zu welchem ​​Problem der sizeof-Ansatz führen kann.

730

Nach vier Jahren gab mir Google diese Antwort. Mit dem standard C++ 11 (alias C++ 0x) gibt es tatsächlich einen neuen angenehmen Weg (zum Preis der Abwärtskompatibilität): das neue Schlüsselwort auto . Es erspart Ihnen den Aufwand, den Typ des zu verwendenden Iterators explizit angeben zu müssen (den Vektortyp erneut zu wiederholen), wenn für den Compiler offensichtlich ist, welcher Typ verwendet werden soll. Wenn v Ihre vector ist, können Sie Folgendes tun:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C++ 11 geht noch weiter und gibt Ihnen eine spezielle Syntax für das Durchlaufen von Sammlungen wie Vektoren. Es beseitigt die Notwendigkeit, Dinge zu schreiben, die immer gleich sind:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

Um es in einem Arbeitsprogramm zu sehen, erstellen Sie eine Datei auto.cpp:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.Push_back(17);
    v.Push_back(12);
    v.Push_back(23);
    v.Push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

Wenn Sie dies mit g ++ kompilieren, müssen Sie es normalerweise so einstellen, dass es mit dem neuen Standard arbeitet, indem Sie ein zusätzliches Flag angeben:

g++ -std=c++0x -o auto auto.cpp

Nun können Sie das Beispiel ausführen:

$ ./auto
17
12
23
42

Bitte beachten Sie, dass die Anweisungen zum Kompilieren und Ausführen spezifisch für den gnu c ++ -Compiler unter Linux sind. Das Programm sollte plattform- und compilerunabhängig sein.

154
kratenko

Im konkreten Fall in Ihrem Beispiel würde ich dazu die STL-Algorithmen verwenden. 

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

Für einen allgemeineren, aber immer noch recht einfachen Fall würde ich mit gehen:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );
43
paxos1977

Zur Antwort von Johannes Schaub:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

Das kann mit einigen Compilern funktionieren, aber nicht mit gcc. Das Problem hier ist die Frage, ob std :: vector :: iterator ein Typ, eine Variable (Member) oder eine Funktion (Methode) ist. Bei gcc erhalten wir folgende Fehlermeldung:

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

Die Lösung verwendet das Schlüsselwort 'typename' wie gesagt:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...
38
Polat Tuzla

Ein Aufruf von vector<T>::size() gibt einen Wert vom Typ std::vector<T>::size_type zurück, nicht int, unsigned int oder anderweitig.

Im Allgemeinen wird die Iteration über einen Container in C++ auch mithilfe von iterators durchgeführt.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Dabei ist T der Datentyp, den Sie im Vektor speichern.

Oder die verschiedenen Iterationsalgorithmen verwenden (std::transform, std::copy, std::fill, std::for_each usw.).

16
Jasper Bekkers

Verwenden Sie size_t:

for (size_t i=0; i < polygon.size(); i++)

Zitieren Wikipedia :

Die Header-Dateien stdlib.h und stddef.h definieren einen Datentyp mit dem Namen size_t, der zur Darstellung der Größe eines Objekts verwendet wird. Bibliotheksfunktionen, die Größen annehmen, erwarten, dass sie vom Typ size_t sind, und der Operator sizeof wird in size_t ausgewertet.

Der tatsächliche Typ von size_t ist plattformabhängig. Ein häufiger Fehler ist die Annahme, dass size_t dasselbe wie das unsignierte int ist, was zu Programmierfehlern führen kann, insbesondere wenn 64-Bit-Architekturen an Bedeutung gewinnen.

11
Igor Oks

Ein bisschen Geschichte:

Um darzustellen, ob eine Zahl negativ ist oder nicht, verwenden Sie ein Vorzeichenbit. int ist ein vorzeichenbehafteter Datentyp, der positive und negative Werte (etwa -2 bis 2 Billionen) enthalten kann. Unsigned kann nur positive Zahlen speichern (und da Metadaten kein bisschen verschwendet werden, können mehr gespeichert werden: 0 bis etwa 4 Milliarden).

std::vector::size() gibt eine unsigned zurück, wie könnte ein Vektor eine negative Länge haben?

Die Warnung weist Sie darauf hin, dass der rechte Operand Ihrer Ungleichheitsaussage mehr Daten enthalten kann als der linke.

Wenn Sie einen Vektor mit mehr als 2 Milliarden Einträgen haben und eine Ganzzahl für die Indexierung verwenden, treten Überlaufprobleme auf (das Int wird wieder auf minus 2 Milliarden zurückgeführt).

6
ecoffey

Ich verwende normalerweise BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

Funktioniert für STL-Container, Arrays, Strings im C-Stil usw.

6
Martin Cote

Die C++ 11-Syntax aktiviert nur eine andere Version für Iteratoren ( ref ):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

Das ist auch für die umgekehrte Iteration bequem

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}
5
Jan Turoň

In C++ 11

Ich würde allgemeine Algorithmen wie for_each verwenden, um die Suche nach dem richtigen Iterator-Typ und Lambda-Ausdruck zu vermeiden, um extra benannte Funktionen/Objekte zu vermeiden.

Das kurze "hübsche" Beispiel für Ihren speziellen Fall (vorausgesetzt, das Polygon ist ein Ganzzahlvektor):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

getestet am: http://ideone.com/i6Ethd

Vergessen Sie nicht include: Algorithmus und natürlich Vektor :)

Microsoft hat eigentlich auch ein schönes Beispiel dazu:
source: http://msdn.Microsoft.com/de-de/library/dd293608.aspx

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.Push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}
5
jave.web
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 
4
Mehrdad Afshari

Der erste ist der richtige Typ und in gewissem Sinne korrekt. (Wenn Sie darüber nachdenken, ist die Größe niemals kleiner als Null.) Diese Warnung erscheint mir jedoch als einer der guten Kandidaten für das Ignorieren.

2
Charlie Martin

Überlegen Sie, ob Sie überhaupt iterieren müssen

Der <algorithm>-Standardheader bietet uns die Möglichkeit dazu:

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

Andere Funktionen in der Algorithmus-Bibliothek führen allgemeine Aufgaben aus - stellen Sie sicher, dass Sie wissen, was verfügbar ist, wenn Sie sich Mühe sparen möchten.

2
Toby Speight

Obskures aber wichtiges Detail: Wenn Sie wie folgt "für (Auto it)" sagen, erhalten Sie eine Kopie des Objekts, nicht das eigentliche Element:

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.Push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

Um die Elemente des Vektors zu ändern, müssen Sie den Iterator als Referenz definieren:

for(auto &it : v)
0
Pierre

Die beiden Codesegmente funktionieren gleich. Die vorzeichenlose int "route ist jedoch korrekt. Die Verwendung von unsignierten int-Typen funktioniert besser mit dem Vektor in der von Ihnen verwendeten Instanz. Wenn Sie die size () - Memberfunktion für einen Vektor aufrufen, wird ein vorzeichenloser Integerwert zurückgegeben "i" auf einen Wert seines eigenen Typs.

Wenn Sie sich immer noch nicht sicher sind, wie "unsigned int" in Ihrem Code aussieht, versuchen Sie "uint". Dies ist im Grunde eine verkürzte Version von "unsigned int" und funktioniert genauso. Sie müssen auch keine anderen Header einfügen, um sie verwenden zu können.

0
user9758081

Wenn Ihr Compiler dies unterstützt, können Sie einen Bereich für den Zugriff auf die Vektorelemente verwenden:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

Drucke: 1 2 3. Beachten Sie, dass Sie diese Technik nicht zum Ändern der Vektorelemente verwenden können.

0
Brett L