This is the second time I make a big mistake, by creating a variable of type bool instead of a double. For example consider
double f()
{
return true;
}
bool something()
{
return 0.0;
}
double g()
{
bool x = 0.0; // (1)
if (something()) {
x = f(); // where f() is a function returning a double
}
return x;
}
I think the compiler should tell me that this is bad, but my compiler (g++) does not issue a small warning with -Wall... and it leads to a bug later in tests. Is there some option for gcc/g++ to have warnings (for example on line 1, that is clearly bad) ?
An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.
bool values are convertible to int type, with true converting to 1 and false converting to 0 . This is guaranteed by the language.
Implicit C++ Type Casting: In implicit C++ type casting, the data type in which the value is to be converted is not specified in the program. It is automatically done by the C++ compiler. When constant values and variables of different types are mixed in an expression, they are converted into the same type.
Implicit type conversion also known as automatic type conversion is carried out by the compiler without the need for a user-initiated action. It takes place when an expression of more than one data type is present which in such an instance type conversion takes place to avoid data loss.
You can use uniform initialization to get an error:
bool x{0.0};
error: type 'double' cannot be narrowed to 'bool' in initializer list [-Wc++11-narrowing]
It can also be used on assignment: x = {f()};
, and returning return {x};
.
Although I don't have a direct answer (a compiler warning was asked for), I do have an opaque typedef library containing an "inconvertibool" type which works like and with bool, but not with other types like int or double. It gives compile-time errors for the cases in your example:
foo.cpp: In function 'double f()':
foo.cpp:5:31: error: cannot convert 'inconvertibool {aka opaque::inconvertibool}' to 'double' in return
return inconvertibool(true);
^
foo.cpp: In function 'inconvertibool something()':
foo.cpp:9:12: error: could not convert '0.0' from 'double' to 'inconvertibool {aka opaque::inconvertibool}'
return 0.0;
^
foo.cpp: In function 'double g()':
foo.cpp:13:23: error: conversion from 'double' to non-scalar type 'inconvertibool {aka opaque::inconvertibool}' requested
inconvertibool x = 0.0; // (1)
^
foo.cpp:15:9: error: no match for 'operator=' (operand types are 'inconvertibool {aka opaque::inconvertibool}' and 'double')
x = f(); // where f() is a function returning a double
^
Of course, this would only help if you consistently use this type instead of bool, but it doesn't quite match your scenario because you said you meant 'double', not 'bool'.
The Visual C++ compiler warns about the conversion to bool
, but with a silly performance warning. Usually it's an undesired warning, but unfortunately it can't be silenced with a simple cast. The almost-idiom for silencing it is to use a double negation, !!
, bang-bang, e.g. return !!0.0
.
Your problem is the opposite, that you want such a warning or error, but still the bang-bang almost-idiom can be part of a solution.
With the idea exemplified below, you simply write Bool
instead of bool
where you want a boolean, and use the !!
to ensure clean bool
values, or else you get compilation errors.
The nice thing about this is that most probably you can just do a global search and replace in your code, replacing bool
with Bool
.
#ifdef CLEAN
# define TO_BOOL !!
#else
# define TO_BOOL
#endif
#define STATIC_ASSERT( e ) static_assert( e, #e )
#include <type_traits> // std::is_same
#include <utility> // std::enable_if_t
class Bool
{
private:
bool value_;
public:
operator bool() const { return value_; }
template< class T
, class Enabled_ = std::enable_if_t<std::is_same<T,bool>::value, void>
>
auto operator=( T const other )
-> Bool&
{ value_ = other; return *this; }
Bool(): value_() {}
template< class T
, class Enabled_ = std::enable_if_t<std::is_same<T,bool>::value, void>
>
Bool( T const value )
: value_( value )
{}
};
auto f()
-> double
{ return 0.0; }
auto something()
-> Bool
{ return TO_BOOL 0.0; } // ← Line 43
auto g()
-> double
{
Bool x = TO_BOOL 0.0; // ← Line 48
if (something()) {
x = TO_BOOL f(); // where f() is a function returning a double
}
return x;
}
auto main() -> int
{
Bool a, b, c;
return a && b || something();
}
Example compilations with g++:
c:\my\forums\so\105> g++ foo.cpp foo.cpp: In function 'Bool something()': foo.cpp:43:22: error: could not convert '0.0' from 'double' to 'Bool' { return TO_BOOL 0.0; } // ← Line 43 ^ foo.cpp: In function 'double g()': foo.cpp:48:25: error: conversion from 'double' to non-scalar type 'Bool' requested Bool x = TO_BOOL 0.0; // ← Line 48 ^ foo.cpp:50:13: error: no match for 'operator=' (operand types are 'Bool' and 'double') x = TO_BOOL f(); // where f() is a function returning a double ^ foo.cpp:23:14: note: candidate: template<class T, class Enabled_> Bool& Bool::operator=(T) auto operator=( T const other ) ^ foo.cpp:23:14: note: template argument deduction/substitution failed: foo.cpp:12:11: note: candidate: Bool& Bool::operator=(const Bool&) class Bool ^ foo.cpp:12:11: note: no known conversion for argument 1 from 'double' to 'const Bool&' foo.cpp:12:11: note: candidate: Bool& Bool::operator=(Bool&&) foo.cpp:12:11: note: no known conversion for argument 1 from 'double' to 'Bool&&' foo.cpp: In function 'int main()': foo.cpp:58:18: warning: suggest parentheses around '&&' within '||' [-Wparentheses] return a && b || something(); ^ c:\my\forums\so\105> g++ foo.cpp -D CLEAN foo.cpp: In function 'int main()': foo.cpp:58:18: warning: suggest parentheses around '&&' within '||' [-Wparentheses] return a && b || something(); ^ c:\my\forums\so\105> g++ foo.cpp -D CLEAN -Wno-parentheses c:\my\forums\so\105> _
If you want implicit conversion from Bool
to some other type than bool
to not be considered, simply make also that conversion operator a checked template, like the constructor and assignment operator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With