Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: "vector<int>::size_type variable" - what is the point of declaring in this way?

I think this is a very basic question but I couldn't just figure it out.

I was used to using arrays in C++ but I'm now starting to learn vectors. I was making a test code, and I came across a question.

First of all, here's the code I made:

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

int main(){
  vector<double> score(10);

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

  double total = accumulate(score.begin(), score.end(),0);

  cout<<"Total score:"<<total<<endl<<"Average score:"<<total/score.size()<<flush;

  return 0;
}

In the for sentence in line #9, I am declaring i as a vector<double>::size_type type (because I was told to do so). I tested the code with the type said above replaced with an int, and it worked perfectly fine. Why is vector<double>::size_type preferred compared to int?

like image 319
coffeeFlow Avatar asked Jul 18 '14 13:07

coffeeFlow


4 Answers

size_type is guaranteed to be large enough for the largest supported vector size, vector::max_size(). int is not: on many common platforms, int has 32 bits, while max_size() is considerably larger than 231.

If you know the size is (and will always be) a small number like 20, then you can get away with using int or any other integer type instead of size_type. If you were to change the program, for example to read the size from the input, then it would go horribly wrong if that value were larger than INT_MAX; while using size_type, it would continue working for any value up to max_size(), which you can easily test for.

like image 156
Mike Seymour Avatar answered Oct 17 '22 05:10

Mike Seymour


The embedded type vector<double>::size_type relates to the return value for various vector methods, such as .size(), hence the preference in that case to use matching types (the int = 0 would generally result in a sign mismatch warning). E.g.

for (vector<double>::size_type i = 0; i < score.size(); ++i) { // types match
  // ...
}

for (int i = 0; i < score.size(); ++i) { // types mismatch
  // ...
}

std::vector::size_type is required to be large enough to represent the maximum number of elements that could be contained in a container, in this case vector<double>::max_size(). In general, it maps to size_t.

In your case, there is no explicit reason to use the vector<double>::size_type (although it would technically be better to use size_type) since the loop runs from 0 to 20 and both are int. Hence the following would be ok.

for (int i = 0; i < 20; ++i) { // loops 20 times
  // ...
}

Additional notes:

Favour iterator based loops over index based ones:

for (vector<double>::iterator i = score.begin(); i != score.end(); ++i) {
  // ...
}

It wasn't tagged as C++11, but if that is possible, the range based for loops deals with a lot of this.

for (double& i : score) {
  // ...
}

Or even using a for_each with a lambda.

like image 37
Niall Avatar answered Oct 17 '22 04:10

Niall


The vector size_type is what's used by vector to do size comparisons. If you had a loop that used an int as a counter and compared against the vector's actual size, you'd get warnings from the compiler about signed vs. unsigned integer comparisons:

for( int i=0; i<score.size(); i++ ){  // <-- signed vs. unsigned comparisons
    // do something...
}
like image 4
pzed Avatar answered Oct 17 '22 03:10

pzed


Your problem is two fold.

First, you are writing beyond the end of the std::vector -- the std::vector has 10 elements, and you are writing to 20.

In order to fix this, and to follow the Don't Repeat Yourself principle, you'd change your code as follows:

int main(){
  std::vector<double> score(20);

  for(std::vector<double>::size_type i=0;i<score.size();i++) {

where I both made the vector larger and used its size to determine how much to write.

Now, when we try to replace that long clause with int:

int main(){
  std::vector<double> score(20);

  for(int i=0;i<score.size();i++) {

we get a singed/unsigned comparison (probably):

i<score.size()

where score.size() is an unsigned value of type std::vector<double>::size_type, and i is an int.

Compilers often give warnings in these cases, as it is really easy to get nonsensical results (if i < 0, the comparison will usually result in the negative number comparing larger than the positive one!) In addition, if the size of the vector is larger than the max value of int (on some systems, as small as 32767, usually at least 2147483647, and sometimes much larger -- this is a value the compiler is reasonably free to pick for itself, the C++ standard does not specify it fully), the loop will fail spectacularly.

Now, while the type of std::vector<double>::size() is std::vector<double>::size_type, this is (in every implementation I've experienced) just std::size_t. So that is a shorter way to say it:

  for(std::size_t i=0;i<score.size();i++) {

(std::size_t is defined in <cstddef> as an aside).

If you are programming in C++11, you can do one better:

int i=0;
for(double& student:score) {
  std::cout<<"Enter marks for student #"<<++i<<":"<<std::flush;
  std::cin>>student;
}

which is a range-based for loop and avoid the problem of indexing entirely.

like image 4
Yakk - Adam Nevraumont Avatar answered Oct 17 '22 03:10

Yakk - Adam Nevraumont