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:
Use int;
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:
- Use
std::reverse_iterator
and do the whole thing in one line:
Like this:
std::string ret(s.rbegin(), s.rend());
Some additional comments:
Don't return a reference to a static string. It's not thread-safe. Return by value (i.e. return a
std::string
, not aconst std::string&
).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:
- It is unsafe to compare signed and unsinged variables (non-constants).
- It is safe to compare 2 unsinged variables of different sizes.
- 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:
- Yes, it is safe to compare
unsigned int
andstd::string::size_type
. - There is no warning because the compiler can perform the safety check (while compiling :)).
- 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
Why "Not All Control Paths Return a Value" Is Warning and Not an Error
Auto' as a Template Argument Placeholder for a Function Parameter
Lambdas and Capture by Reference Local Variables:Accessing After the Scope
Ramifications of C++20 Requiring Two's Complement
Remote Debugging C++ Applications with Eclipse Cdt/Rse/Rdt
How to Convert from Lpctstr to Std::String
Static Variable in the Class Declaration or Definition
Initialize a Vector to Zeros C++/C++11
Adding Multiple Executables in Cmake
Using Libstdc++ Compiled Libraries with Clang++ -Stdlib=Libc++
What Is the Use of "Using Namespace Std"
Should Every Class Have a Virtual Destructor
How to Allocate a Specific Memory Address Using Pointers in C++