C++ for-loop - size_type vs. size_t

2020-01-26 07:20发布

问题:

In the C++ Primer book, Chapter (3), there is the following for-loop that resets the elements in the vector to zero.

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

Why is it using vector<int>::size_type ix = 0? Cannot we say int ix = 0? What is the benefit of using the first form on the the second?

Thanks.

回答1:

The C++ Standard says,

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

Then it adds,

Implementations of containers described in this International Standard are permitted to assume that their Allocator template parameter meets the following two additional requirements beyond those in Table 32.

  • The typedef members pointer, const_pointer, size_type, and difference_type are required to be T*,T const*, size_t, and ptrdiff_t, respectively

So most likely, size_type is a typedef of size_t.

And the Standard really defines it as,

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

So the most important points to be noted are :

  • size_type is unsigned integral, while int is not necessarily unsigned. :-)
  • it can represent the largest index, because it's unsigned.


回答2:

Yes you can use int, but only the type vector<int>::size_type guarantees that its type can be used to index all vector elements.

It may or may not be the same size as int. Eg, when compiling for 64-bit Windows, int is 32-bit wide, whereas vector<int>::size_type will be 64-bit wide.

Instead of using the rather verbose vector<int>::size_type, you could use std::size_t, as the former is a typedef for the latter. However, if you ever happen to change the container type, then its size_type maybe a different type, and you may have to modify your code if it uses std::size_t.



回答3:

vector<int>::size_type is a type that is guaranteed to hold the size of the biggest vector you may have, and thus it's guaranteed to let you index all the elements of the vector (since indexes go from 0 to size-1); it is the type used for indexes and sizes in all the vector methods.

If you have very big arrays this may be actually relevant, since other integer types may overflow (and if they are signed types things can get quite strange); even if you won't ever get to arrays so big that this may matter, it's fundamentally a code cleanliness thing; moreover, your ix has the same type of ivec.size(), so you don't get warnings for comparing signed and unsigned integers.

Background: vector<T>::size_type is usually a typedef for size_t (I read somewhere that actually the standard implicitly imposes it to be size_t - EDIT: it's not implicit at all, see @Nawaz's answer), which, in turn, is the return type of the sizeof operator. This implicitly says that it can hold the size for the biggest object usable in a C++ application, so it is surely (just) big enough to index arrays of any type.

Actually, I use size_t (defined in <cstddef>) as index also for C-style arrays, and I think it's good practice for exactly the same reasons.


By the way, you may also forget of the type used for indexes altogether and just go with iterators:

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

or with iterators+<algorithm>:

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

These two options work whatever container ivec is, so you don't have to change anything in the code if you decide to change container type.

With vector you can also use the assign method (as suggested in some other answer):

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


回答4:

You should not use intbecause vector<int>::size_type is an unsigned type, that is, vector indexes its elements with an unsigned type. int however is a signed type and mixing signed and unsigned types can lead to weird problems. (Although it would not be a problem for small n in your example.)

Note that I think it's clearer to just use size_t (as opposed to T::size_type) - less typing and should work on all implementations.

Note also that the for loop you posted:

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

would be better written as:

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

-- no need to call size() every iteration.