String::Size_Type Instead of Int

string::size_type instead of int

A short holds numbers too. As does a signed char.

But none of those types are guaranteed to be large enough to represent the sizes of any strings.

string::size_type guarantees just that. It is a type that is big enough to represent the size of a string, no matter how big that string is.

For a simple example of why this is necessary, consider 64-bit platforms. An int is typically still 32 bit on those, but you have far more than 2^32 bytes of memory.

So if a (signed) int was used, you'd be unable to create strings larger than 2^31 characters.
size_type will be a 64-bit value on those platforms however, so it can represent larger strings without a problem.

Iterating over a string with string::size_type vs int

string::size_type is an unsigned type, which means it can never be negative. That means this condition:

i >= 0

will never be false. In fact, when i goes below 0, it will wrap around, and become the largest possible value of string::size_type. Then when you index x[i], you invoke undefined behaviour.

I would suggest writing the loop this way:

for(int i = static_cast<int>(x.size()) - 1; i >= 0; i--)
// ...

Note the static_cast before the subtraction by 1 (otherwise you'll have issues with an empty string).

Unless you have really large strings, the length should be well within what an int can handle. If you do need a string::size_type index, you could do this loop as well:

auto i = x.size();
while (i-- > 0)
// ...

Note this is robust to unsigned types. The comparison against 0 happens before the decrement of i (hence the post-decrement operator). So you nicely get the indices size-1, size-2, ..., 1,0. When i is 0, the comparison will fail, and then the fact that i becomes a big positive number doesn't matter.

difference between size_type and int

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.

int works but string::size_type doesn't

It's because string::size_type is unsigned, so the for-loop's terminating condition i >= 0 will always be true. Your choices:

  1. Use int;

  2. Continue using size_type but change your loop:

    for(string::size_type i = s.length(); i > 0; --i) {
    ret += s[i-1];
    }

Or my favoured choice:


  1. Use std::reverse_iterator and do the whole thing in one line:

Like this:

std::string ret(s.rbegin(), s.rend());

Some additional comments:

  1. Don't return a reference to a static string. It's not thread-safe. Return by value (i.e. return a std::string, not a const std::string&).

  2. For better performance, preallocate the required capacity for the string: ret.reserve(s.size());.

vectorint::size_type in C++

size_type is a (static) member type of the type vector<int>. Usually, it is a typedef for std::size_t, which itself is usually a typedef for unsigned int or unsigned long long.

Why does the address of a size_type variable is used as an argument of stoi() in C++?

First, it is not mandatory, it can be NULL.
The contribution is for case when your string contains several values. This allows to parse them one by one. After a call to stoi, *idx will contain the start index of the next integer.
For example:

int main() {
std::string str = "23 45 56 5656";
std::string::size_type off = 0;
do {
std::string::size_type sz;
cout << std::stoi(str.substr(off), &sz) << endl;
off += sz;
} while (off < str.length());
}

// will print
// 23
// 45
// 56
// 5656

EDIT: as @Surt correctly commented, some error handling can and should added here. So lets make this example complete. The function stoi can throw either invalid_argument or out_of_range, these exceptions should be handled. How to handle them - IDK, your decision here is an example:

int main() {
std::string str = "23 45 56 5656 no int";
std::string::size_type off = 0;
try {
do {
std::string::size_type sz;
std:cout << std::stoi(str.substr(off), &sz) << std::endl;
off += sz;
} while (off < str.length());
} catch(const std::invalid_argument &e) {
std::cout << "Oops, string contains something that is not a number"
<< std::endl;
} catch(const std::out_of_range &e) {
std::cout << "Oops, some integer is too long" << std::endl;
}
}

Is string::size_type really big enough to hold any string?

string::size_type is guaranteed to be large enough for all strings that the current implementation supports - not any string of any size. If your implementation supports strings up to e.g. 8 GB, size_type can contain numbers up to 8 billion.

Is it safe to compare an unsigned int with a std::string::size_type

From the compiler point of view:

  1. It is unsafe to compare signed and unsinged variables (non-constants).
  2. It is safe to compare 2 unsinged variables of different sizes.
  3. It is safe to compare an unsigned variable with a singed constant if the compiler can check that constant to be in the allowed range for the type of the signed variable (e.g. for 16-bit signed integer it is safe to use a constant in range [0..32767]).

So the answers to your questions:

  1. Yes, it is safe to compare unsigned int and std::string::size_type.
  2. There is no warning because the compiler can perform the safety check (while compiling :)).
  3. There is no problem to use different unsigned types in comparison. Use unsinged int.

Handling int and std::vector::size_type in comparsion

Your code looks a bit too perfect for my taste.

Breaking it down:

const int res = std::stoi(cmdarg);
if (res >= 0 && static_cast<std::vector<MyClass*>::size_type>(res) < this->container.size())

The if-statement for checking below zero is nice. Personally I would write this as:

if (res < 0)
{
std::cerr << "Negative number " << res <<" given for ..., a positive was expected" << std::endl;
return -1;
}

This leads us to the cast:

auto unsigned_res = static_cast<std::vector<MyClass*>::size_type>(res);

However, size_type this vector always size_t as it uses the std::allocator. In code review, I would request to change this for readability to:

auto unsigned_res = static_cast<std::size_t>(res);

Finally, you can indeed nicely compare it:

if (unsiged_res < container.size())
// Do something

Note that I mentioned both the comparison and the cast, as this needs to happen in that order. On top of that, you also need some exception handling for when std::stoi fails, see it's documentation

For more details on how to correctly deal with signed/unsigned, I can recommend this article on ithare.



Related Topics



Leave a reply



Submit