Итерация по std :: vector: unsigned vs знаковая переменная со знаком - PullRequest
435 голосов
/ 03 января 2009

Как правильно перебирать вектор в C ++?

Рассмотрим эти два фрагмента кода, этот работает нормально:

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

и вот этот:

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

, который генерирует warning: comparison between signed and unsigned integer expressions.

Я новичок в мире C ++, поэтому переменная unsigned кажется мне несколько пугающей, и я знаю, что unsigned переменные могут быть опасны, если не используются правильно, поэтому - это правильно?

Ответы [ 16 ]

747 голосов
/ 03 января 2009

Итерация в обратном направлении

См. этот ответ .

Итерация вперед

Это почти идентично. Просто измените итераторы / своп декремент на приращение. Вы должны предпочесть итераторы. Некоторые люди советуют вам использовать std::size_t в качестве типа переменной индекса. Однако это не переносимо. Всегда используйте size_type typedef контейнера (хотя вы можете избежать только преобразования в прямом итеративном случае, на самом деле он может пойти не так в обратном цикле при использовании std::size_t, в случае std::size_t шире, чем typedef для size_type):

Использование std :: vector

Использование итераторов

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

Важно, всегда используйте форму приращения префикса для итераторов, определения которых вы не знаете. Это гарантирует, что ваш код работает как можно более универсальным.

Использование диапазона C ++ 11

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

Использование индексов

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

Использование массивов

Использование итераторов

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

Использование диапазона C ++ 11

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

Использование индексов

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

Прочтите в обратной итерации ответ, к какой проблеме может привести подход sizeof.

159 голосов
/ 05 марта 2013

Прошло четыре года, Google дал мне этот ответ. С стандартом C ++ 11 (он же C ++ 0x ) фактически появился новый приятный способ сделать это (ценой нарушения обратной совместимости). ): новое ключевое слово auto. Это избавляет вас от необходимости явно указывать тип используемого итератора (повторяя векторный тип), когда очевидно (для компилятора), какой тип использовать. С v, являющимся вашим vector, вы можете сделать что-то вроде этого:

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

C ++ 11 идет еще дальше и дает вам специальный синтаксис для перебора коллекций, таких как векторы. Это устраняет необходимость писать вещи, которые всегда одинаковы:

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

Чтобы увидеть это в работающей программе, соберите файл 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;
}

На момент написания этого, когда вы компилируете это с g ++ , вам обычно нужно настроить его на работу с новым стандартом, задав дополнительный флаг:

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

Теперь вы можете запустить пример:

$ ./auto
17
12
23
42

Обратите внимание , что инструкции по компиляции и запуску относятся только к gnu c ++ компилятору на Linux , программа должна быть независимой от платформы (и компилятора).

44 голосов
/ 03 января 2009

В конкретном случае в вашем примере я бы использовал алгоритмы STL для достижения этой цели.

#include <numeric> 

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

Для более общего, но все же довольно простого случая, я бы сказал:

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

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );
38 голосов
/ 27 февраля 2010

Относительно ответа Иоганна Шауба:

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

Это может работать с некоторыми компиляторами, но не с gcc. Проблема здесь заключается в том, является ли std :: vector :: iterator типом, переменной (членом) или функцией (методом). Мы получаем следующую ошибку с gcc:

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

Решение использует ключевое слово typename, как сказано:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...
16 голосов
/ 03 января 2009

Вызов vector<T>::size() возвращает значение типа std::vector<T>::size_type, а не int, unsigned int или иное.

Также обычно итерация над контейнером в C ++ выполняется с использованием итераторов , например:

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

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

Где T - тип данных, которые вы храните в векторе.

Или с использованием различных алгоритмов итерации (std::transform, std::copy, std::fill, std::for_each и так далее).

11 голосов
/ 03 января 2009

Использование size_t:

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

Цитата Википедия :

Заголовочные файлы stdlib.h и stddef.h определяют тип данных с именем size_t, который используется для представления размера объекта. Библиотечные функции, принимающие размеры, ожидают, что они будут иметь тип size_t, а оператор sizeof оценивается как size_t.

Фактический тип size_t зависит от платформы; Распространенной ошибкой является допущение, что size_t совпадает с unsigned int, что может привести к ошибкам программирования, особенно из-за того, что 64-разрядные архитектуры становятся более распространенными.

6 голосов
/ 03 января 2009

Я обычно использую BOOST_FOREACH:

#include <boost/foreach.hpp>

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

Работает с контейнерами STL, массивами, строками в стиле C и т. Д.

6 голосов
/ 03 января 2009

Немного истории:

Чтобы показать, является ли число отрицательным или нет, компьютер использует бит «знак». int - это тип данных со знаком, означающий, что он может содержать положительные и отрицательные значения (от -2 миллиардов до 2 миллиардов). Unsigned может хранить только положительные числа (и поскольку он не тратит немного времени на метаданные, он может хранить больше: от 0 до около 4 миллиардов).

std::vector::size() возвращает unsigned, как вектор может иметь отрицательную длину?

Предупреждение говорит о том, что правый операнд вашего оператора неравенства может содержать больше данных, чем левый.

По существу, если у вас есть вектор с более чем 2 миллиардами записей и вы используете целое число для индексации, вы столкнетесь с проблемами переполнения (int вернется к отрицательному 2 миллиардам).

5 голосов
/ 12 ноября 2014

В С ++ 11

Я бы использовал общие алгоритмы, такие как for_each, чтобы избежать поиска правильного типа итератора и лямбда-выражения, чтобы избежать дополнительных именованных функций / объектов.

Короткий "симпатичный" пример для вашего конкретного случая (предполагается, что многоугольник - это вектор целых чисел):

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

проверено на: http://ideone.com/i6Ethd

Не забудьте включить: алгоритм и, конечно же, вектор:)

У Microsoft также есть хороший пример этого:
источник: http://msdn.microsoft.com/en-us/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 голосов
/ 29 октября 2014

В завершение синтаксис C ++ 11 позволяет использовать только одну версию для итераторов ( ref ):

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

Что также удобно для обратной итерации

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...