С++ for-loop - size_type vs. size_t

В C++ Primer книге, глава (3), существует следующий цикл for, который сбрасывает элементы в векторе до нуля.

for (vector<int>::size_type ix = 0; ix ! = ivec.size(); ++ix)
ivec[ix] = 0;

Почему используется vector<int>::size_type ix = 0? Разве мы не можем сказать int ix = 0? Какая польза от использования первой формы на второй?

Спасибо.

Ответ 1

В стандарте С++ говорится:

 size_type  |  unsigned integral type  |  a type that can represent the size of the largest object in the
allocation model

Затем он добавляет:

Реализации контейнеров описанных в этом Международном Стандарту разрешено предположить, что их параметр шаблона Allocator отвечает следующим двум дополнительным чем те, что указаны в таблице 32.

  • Указатель членов typedef, const_pointer, size_type и Разница для T *, T const *, size_t и ptrdiff_t, соответственно

Так что, скорее всего, size_type является typedef из size_t.

И стандарт действительно определяет его как

template <class T> 
class allocator 
{
   public:
       typedef size_t size_type;
       //.......
};

Поэтому наиболее важные моменты, которые необходимо отметить, следующие:

  • size_type является интегралом unsigned, а int не обязательно unsigned.: -)
  • он может представлять наибольший индекс, потому что он неподписан.

Ответ 2

Да, вы можете использовать int, но только тип vector<int>::size_type гарантирует, что его тип может использоваться для индексации всех векторных элементов.

Он может быть или не быть такого же размера, как int. Например, при компиляции для 64-разрядной Windows int имеет 32-разрядную ширину, тогда как vector<int>::size_type будет 64-битным.

Вместо использования довольно подробного vector<int>::size_type, вы можете использовать std::size_t, поскольку первый для него является typedef. Однако, если вам когда-либо случится изменить тип контейнера, тогда его size_type может быть другого типа, и вам, возможно, придется изменить свой код, если он использует std::size_t.

Ответ 3

vector<int>::size_type - это тип, который гарантированно удерживает размер наибольшего vector, который у вас может быть, и, следовательно, он гарантированно позволит вам индексировать все элементы vector (поскольку индексы идут от 0 до размера -1); это тип, используемый для индексов и размеров во всех методах vector.

Если у вас очень большие массивы, это может быть действительно актуально, так как другие целые типы могут переполняться (а если они signed, все может стать довольно странным); даже если вы никогда не доберетесь до таких больших массивов, что это может иметь значение, это принципиально вещь чистоты кода; кроме того, ваш ix имеет тот же тип ivec.size(), поэтому вы не получите предупреждения для сравнения целых чисел без знака.

Фон: vector<T>::size_type обычно typedef для size_t (я где-то читал, что на самом деле стандартный неявно налагает его на size_t - EDIT: он вообще не подразумевается, см. ответ @Nawaz), который, в свою очередь, является возвращаемым типом оператора sizeof. Это подразумевает, что он может содержать размер для самого большого объекта, который можно использовать в приложении на С++, поэтому он (достаточно) достаточно велик, чтобы индексировать массивы любого типа.

На самом деле, я использую size_t (определенный в <cstddef>) как индекс также для массивов в стиле C, и я думаю, что это хорошая практика по точно таким же причинам.


Кстати, вы также можете забыть тип, используемый для индексов, и просто пойти с итераторами:

for (vector<int>::iterator it = ivec.begin(); it != ivec.end(); ++it)
    *it = 0;

или с итераторами + <algorithm>:

std::fill(ivec.begin(), ivec.end(), 0);

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

С помощью vector вы также можете использовать метод assign (как предлагается в другом ответе):

ivec.assign(ivec.size(), 0);

Ответ 4

Вы не должны использовать int, потому что vector<int>::size_type - это неподписанный тип, то есть вектор индексирует его элементы с неподписанным типом. int однако является подписанным типом, и смешение подписи и неподписанных типов может привести к странным проблемам. (Хотя это не будет проблемой для небольшого n в вашем примере.)

Обратите внимание, что я считаю, что проще использовать size_t (в отличие от T:: size_type) - меньше набирать текст и должно работать на всех реализациях.

Обратите внимание, что цикл for, который вы отправили:

for(size_t ix=0; ix != ivec.size(); ++ix) ...

лучше писать как:

for(size_t i=0, e=ivec.size(); i!=e; ++ix) ...

- не нужно вызывать size() каждую итерацию.