Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

difference between size_type and int

Tags:

c++

stl

vector

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

int main()
{
    vector<double> student_marks(20);

    for (vector<double>::size_type i = 0; i < 20; i++)
    {
        cout << "Enter marks for student #" << i+1 
            << ": " << flush;
        cin >> student_marks[i];
    }
    return 0;
}

I read somewhere that it is better to use size_type in place of int . Does it really make a huge impact on the implementation and what are the positives of using size_type ?

like image 571
abkds Avatar asked Mar 16 '14 13:03

abkds


3 Answers

vector<double>::size_type is guaranteed to cover the full range of possible values of the size of a vector<double>. An int is not.

Note that vector<double>::size_type is usually the same as std::size_t, so in general it would be OK to use the latter. However, custom allocators can result in the a vector with a size_type different to std::size_t.

like image 164
juanchopanza Avatar answered Oct 24 '22 01:10

juanchopanza


size_type is an unsigned number. This means it cannot be negative. Which may sound a logical choice for container sizes, but in real life creates a lot of problems.

For example, when you substract one size_type from another, the result will silently be converted to a huge positive number if the second operand is greater than the first:

std::vector<double>::size_type size1 = v1.size();
std::vector<double>::size_type size2 = v2.size();
if ((size1 - size2) > 5) // dangerous if v2 is bigger than v1!
{
    // ...
}

And it often makes error checking impossible:

void f(std::vector<double>::size_type size)
{
    if (size < 0)
    {
        // error handling will never be reached    
    }
}

f(-1); // no error handling

It is for such problems that you usually prefer (signed) int to unsigned in your code, and it is unfortunate that standard library containers express their sizes as unsigned numbers. You will often want to convert (static_cast) sizes to signed numbers as soon as possible. In your example, it's fine to stay unsigned, because it's just the loop index, and casting to int first would be over-engineering IMO.

The good news is that you can often avoid size_type altogether by using iterators:

for (std::vector<double>::const_iterator iter = v.begin(); iter != v.end(); ++iter)
{
    std::cout << *iter;
}

I invite you to have a look at Signed and Unsigned Types in Interfaces by Scott Meyers.

Finally, please note that this point of view is not universally shared by experienced C++ programmers. Searching for past discussions about this topic on the web (old Stackoverflow questions, comp.lang.c++ Usenet archive etc) will reveal different opinions.

like image 45
Christian Hackl Avatar answered Oct 24 '22 01:10

Christian Hackl


The positives of using size_type, as opposed to size_t, is just conformity. It pleases those who learn by rote and reason purely by association. They do like they see others like themselves do, and want to have that be perceived as “right”.

The size_type member is a customization point for the container’s size type. It comes from the allocator template parameter. With the default standard allocator it's just a synonym for size_t, i.e. these are, by default, the same type.

The problem with int, as opposed to the signed type ptrdiff_t (the result type for a pointer difference), is that int may not have sufficient range for a really huge vector on a 64-bit system.

The problem with size_t is that it can easily cause inadvertent use of modulo arithmetic in mixed type expressions. For example, the expression std::string( "Hi!" ).length() < -7 will always yield true. Thus it's a bug-attractor, so ptrdiff_t, or just plain int when you're sure of the range, is very much objectively preferable (even though it may displease the aforementioned folks enormously – there is still a very strong social pressure to use the objectively inferior solution).

In the standard library the unsigned type size_t is used for historical reasons.

In particular, on 16-bit systems memory was a very limited resource, and one had to use every little dirty trick to make use of absolutely all of it – for those small systems, in those days, it was well worth the risk of some wrap-around bugs etc. Today it isn't.

like image 44
Cheers and hth. - Alf Avatar answered Oct 24 '22 02:10

Cheers and hth. - Alf