Home > Software engineering >  signed/unsigned mismatch when calling a function, but not when "inline"
signed/unsigned mismatch when calling a function, but not when "inline"

Time:05-03

For this snippet

   using T = int;
   const std::vector<T> v;
   if (v.size() != 1) {} // could call operator!=()

the code complies cleanly even at high warnings levels (all warnings enabled in Visual Studio 2022). However, if I pass the arguments to a function

   const auto f = [](auto&& lhs, auto&& rhs) { if (lhs != rhs) {}};
   f(v.size(), 1);

the compiler generates a '!=': signed/unsigned mismatch warning.

How can I make the function behave the same way as the "inline" code, i.e., no warning for arbitrary types?


Keep in mind that the "real code" is something like

#define f(lhs, rhs) if (lhs != rhs) {}

I'd like to replace the macro with a function

temmplate<typename TLhs, typename TRhs>
inline void f(TLhs&& lhs, TRhs&& rhs)
{
   if (lhs != rhs) {}
}

CodePudding user response:

In the direct comparison the compiler knows the type of both side of the comparison, and can therefore make sure that the 1 will be an unsigned type of the correct size.

When you call your lambda the type for rhs will be deduced as an int, because that's what 1 really is. And since lhs will be an unsigned type, you get the warning in the comparison when you compare the unsigned size with the (signed) int value 1.


You will get the same problem with any kind of callable object where the compiler must deduce the arguments, like for example function templates:

template<typename T, typename U>
void f(T const& lhs, U const& rhs);

For a function template the solution is simple: Use the same single type for both arguments:

template<typename T>
void f(T const& lhs, T const& rhs);

CodePudding user response:

Your first example is a specific case. So even though you're comparing a signed int to an unsigned std::vector::size_type, the compiler tell that there's no problem. It will implicitly convert 1 to the unsigned type, which has no problem representing a 1.

In the second example, you have the general case. When instantiating the lambda function for the (size_type, int) case, it has to be prepared for any set of values. It cannot assume that the value of the int can be converted to the size_type without altering the value. (In theory, it could inline the lambda, and then it would see the specific values in this particular invocation, but apparently it doesn't do that.)

The solution is to make the two parameters identical (or, at least, compatible).

f(v.size(), 1u);
  • Related