简体   繁体   中英

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

I am going trough the book "Accelerated C++" by Andrew Koenig and Barbara E. Moo and I have some questions about the main example in chap 2. The code can be summarized as below, and is compiling without warning/error with g++:

#include <string>
using std::string;

int main()
{
    const string greeting = "Hello, world!";
    // OK
    const int pad = 1;
    // KO
    // int pad = 1;
    // OK
    // unsigned int pad = 1;
    const string::size_type cols = greeting.size() + 2 + pad * 2;
    string::size_type c = 0;
    if (c == 1 + pad)
    {;}

    return 0;
}

However, if I replace const int pad = 1; by int pad = 1; , the g++ compiler will return a warning:

warning: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
    if (c == 1 + pad)

If I replace const int pad = 1; by unsigned int pad = 1; , the g++ compiler will not return a warning.

I understand why g++ return the warning, but I am not sure about the three below points:

  • Is it safe to use an unsigned int in order to compare with a std::string::size_type ? The compiler does not return a warning in that case but I am not sure if it is safe.
  • Why is the compiler not giving a warning with the original code const int pad = 1 . Is the compiler automatically converting the variable pad to an unsigned int ?
  • I could also replace const int pad = 1; by string::size_type pad = 1; , but the meaning of the variable pad is not really linked to a string size in my opinion. Still, would this be the best approach in that case to avoid having different types in the comparison?

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 (eg 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 .

If you are using std::string with the default allocator (which is likely), then size_type is actually size_t .

[support.types]/6 defines that size_t is

an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.

So it's not technically guaranteed to be a unsigned int , but I believe it is defined this way in most cases.

Now regarding your second question: if you use const int something = 2 , the compiler sees that this integer is a) never negative and b) never changes, so it's always safe to compare this variable with size_t . In some cases the compiler may optimize the variable out completely and simply replace all it's occurrences with 2 .

I would say that it is better to use size_type everywhere where you are to the size of something, since it is more verbose.

Comparing signed and unsigned values is "dangerous" in the sense that you may not get what you expect when the signed value is negative (it may well behave as a very large unsigned value, and thus a > b gives true when a = -1 and b = 100 . (The use of const int works because the compiler knows the value isn't changing and thus can say "well, this value is always 1, so it works fine here")

As long as the value you want to compare fits in unsigned int (on typical machines, a little over 4 billion) is fine.

What the compiler warns about is the comparison of unsigned and signed integer types. This is because the signed integer can be negative and the meaning is counter intuitive. This is because the signed is converted to unsigned before comparison, which means the negative number will compare greater than the positive .

Is it safe to use an unsigned int in order to compare with a std::string::size_type? The compiler does not return a warning in that case but I am not sure if it is safe.

Yes, they are both unsigned and then the semantics is what's expected. If their range differs the narrower are converted to a wider type.

Why is the compiler not giving a warning with the original code const int pad = 1. Is the compiler automatically converting the variable pad to an unsigned int?

This is because how the compiler is constructed. The compiler parses and to some extent optimizes the code before warnings are issued. The important point is that at the point this warning is being considered the compiler nows that the signed integer is 1 and then it's safe to compare with a unsigned integer.

I could also replace const int pad = 1; by string::size_type pad = 1;, but the meaning of the variable pad is not really linked to a string size in my opinion. Still, would this be the best approach in that case to avoid having different types in the comparison?

If you don't want it to be constant the best solution would probably be to make it at least an unsigned integer type. However you should be aware that there is no guaranteed relation between normal integer types and sizes, for example unsigned int may be narrower, wider or equal to size_t and size_type (the latter may also differ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM