c++language-lawyerstd

How to compare size_t and difference_type?


The code below

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = { 1,2,3 };

    if (std::count(v.begin(), v.end(), 1) > v.size() / 2) {
        std::cout << "Too many\n";
    } 
}

gives a warning:

(8,40) warning C4018: '>': signed/unsigned mismatch

which is quite reasonable, since std::count returns ::difference_type and size() returns ::size.

The question is, what is the robust way to handle this?

In the Comparing ptrdiff_t with size_t it is discussed how this should be implemented in compilers, but never said how to safely live with existing implementations.

For example, it is stated:

If ptrdiff_t is of greater rank than size_t and can represent all positive values of size_t. limit - buf <= sizeof buf poses no problems then.

Else ptrdiff_t may not represent all positive values of size_t and then the subtraction limit - buf may be UB per below, so the compare is moot.

Great, but under Win64 we have:

typedef unsigned __int64 size_t;
typedef __int64          ptrdiff_t;

And this is a direct indications (if containers don't use other types for ::difference_type) that size_t values won't fit to ptrdiff_t for safe calculations (it can't represent all size_t values as positive).

So, this:

    if (std::count(v.begin(), v.end(), 1) > static_cast<std::vector<int>::difference_type>(v.size() / 2)) {
        std::cout << "Too many\n";
    }

is not safe anymore.

Casting in the opposite way is unacceptable since it violates first guidance above.

So, what is the safest way to shape the code here?

Should I write a wrapper to check the sizes and return the largest type or there is something more reasonable and built-in?


Solution

  • The issue is between a signed and unsigned integer value. Vector does not have an unsigned ssize function. Instead, use the ssize() from the standard or ranges library.

    auto main() -> int {
    
       std::vector<int> v = { 1,2,3 };
    
       if (std::count(v.begin(), v.end(), 1) > std::ssize(v) / 2) {
          std::cout << "Too many\n";
       }
    
       return 0;
    }