Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ implicit conversion from double to bool is dangerous

Tags:

c++

gcc

g++

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) ?

like image 884
Renaud Avatar asked Mar 12 '16 16:03

Renaud


People also ask

What happens in implicit conversion?

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.

Can bool be converted to int?

bool values are convertible to int type, with true converting to 1 and false converting to 0 . This is guaranteed by the language.

Does C++ do implicit casting?

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.

What is an implicit conversion in C++?

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.


3 Answers

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};.

like image 173
emlai Avatar answered Nov 17 '22 22:11

emlai


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'.

like image 28
Kyle Markley Avatar answered Nov 17 '22 21:11

Kyle Markley


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.

like image 31
Cheers and hth. - Alf Avatar answered Nov 17 '22 21:11

Cheers and hth. - Alf