Warning when comparing references to int and unsig

2019-09-11 12:08发布

问题:

So I'm writing the doctest library and it's supposed to be warning free.

I recently noticed that code that went into an if statement without warnings was causing trouble when written inside of my CHECK() macro.

For example: if(0u == 0) doesn't cause a warning but CHECK(0u == 0); does.

I investigated a bit and part of the reason is that behind the CHECK() macro there are templates and expression decomposition and capturing by const reference.

My questions are 3:

  1. Given these 3 snippets - why does this happen?

gives a warning:

int a = 0;
unsigned b = 0;
if(a == b)

doesn't give a warning:

const int a = 0;
const unsigned b = 0;
if(a == b)

gives a warning:

const int& a = 0;
const unsigned& b = 0;
if(a == b)
  1. How can I combat this? Obviously I can use #pragma diagnostic around the templates in the library header and silence these warnings but this will not be correct.

The reason is that if the following code gives a warning:

int a = 0;
unsigned b = 0;
if(a == b)

then the next piece of code should also give a warning:

int a = 0;
unsigned b = 0;
CHECK(a == b);
  1. Is there something I'm missing about the const noref case? Because yesterday I posted this question which seems strikingly similar... Are there other cases that will bite me by the ass because of this capturing by const reference in my templates?

I don't think the compiler or the optimization levels matter - I've tried a few versions of g++/MSVC (/W4 for msvc and -Wall -Wextra -pedantic + 50 more for g++) and probably clang does the same...

EDIT:

The following code produced a warning with g++ but did NOT with msvc... (-Wsign-conversion)

const int a = -1;
const unsigned b = 0;
if(a == b)

回答1:

Usually a unsigned int can represent larger values than an int. While you can convert an unsigned int to an int, there are cases where it will fail. For example, in a 2 complement notation, a -1 is converted to the largest number in a unsigned int (assuming that both are using a container / register of same size).

Note that this is true when you use const to reference, because some references can be initialized at runtime. For example, when you say that a function has in its arguments const references. You will only knows its values at calling times.

By the other hand, a const int or const unsigned int has its values known at compile time. Compiler knows how to convert from which other and there is no sides effects, so no warning is required.

How to combat this situation? Just use type that are equal between them. If you really want to use different types and know the side effects, you can tell compiler that you know what you are doing and do a cast (static_cast).



回答2:

It is perfectly normal to get warnings when comparing 2 variables, one signed, one unsigned. For constants it is different:

This code does not give a warning because a and b are evaluated at compile time and both replaced by 0.

Take this piece of code and compile it:

const int a = 0;
const unsigned b = 0;
if(a == b)
{
   c =15;
}
else
{
   c=67;
}

 cout << c << endl;

Disassembly around the a==b part:

if(a == b)
{
        c=15;
  1d:   c7 45 f4 0f 00 00 00    movl   $0xf,-0xc(%rbp)
else
{
        c = 67;
}
cout << c << endl;

The test is skipped, the else is skipped... why would the compiler issue a warning?

This behaviour is sometimes used by developpers to disable parts of code: in the aeronautics business, unreachable code is forbidden. This mechanism ensures that no code is generated. An audit can prove there's no dead code at machine code level.