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:
- 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)
- 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);
- 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)
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
andb
are evaluated at compile time and both replaced by 0.Take this piece of code and compile it:
Disassembly around the
a==b
part: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.
Usually a
unsigned int
can represent larger values than anint
. While you can convert anunsigned int
to anint
, there are cases where it will fail. For example, in a 2 complement notation, a -1 is converted to the largest number in aunsigned 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
orconst 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).