7

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

sepp2k
  • 363,768
  • 54
  • 674
  • 675
Renaud
  • 209
  • 2
  • 9
  • Have you tried `-pedantic-errors`? – Violet Giraffe Mar 12 '16 at 16:14
  • I just tried with : g++ -Wall -Wextra -pedantic-errors -c dummy.cpp and I still don't have warnings. – Renaud Mar 12 '16 at 16:19
  • MSVC 14 does generate [C4800](https://msdn.microsoft.com/en-us/library/b6801kcy.aspx) for this. – wally Mar 12 '16 at 16:21
  • Yep, [no warnings in this case](http://coliru.stacked-crooked.com/a/8220306a87d9e914) with g++. :( – wally Mar 12 '16 at 16:25
  • And clang [doesn't have any complaints](http://coliru.stacked-crooked.com/a/85148936c9436621) either. – wally Mar 12 '16 at 16:28
  • Personally, I think that code readability, rather than performance, is the main reason to avoid code like that. `x = (f() != 0.0);` is much easier to understand IMO. – Christian Hackl Mar 12 '16 at 16:39
  • i had the same issue. i am curious what is the implicit conversion. incidentally, if you declare a variable as double and use it as if it's a bool, everything just works. probably a bug waiting to happen. – thang Sep 02 '19 at 03:39

4 Answers4

3

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

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • I dont have a warning with bool x{0.0}; I am using mingw-w64 with gcc-5.2 – Renaud Mar 12 '16 at 16:20
  • 1
    @Renaud For some reason gcc doesn't implement this behavior. I'd recommend using clang if you want to use this feature. – Emil Laine Mar 12 '16 at 16:29
  • @Renaud: This seems to bug in gcc. [http://melpon.org/wandbox/permlink/5vqO6rRq8x8sNiix](http://example.com) – Destructor Mar 12 '16 at 16:32
  • I have a narrowing warning if I do int y{7.3}; but not for bool x{0.0}; Also -Wc++11-narrowing is not available on my gcc (5.2). – Renaud Mar 12 '16 at 16:32
  • 1
    @Renaud: See this also: http://stackoverflow.com/questions/27507361/narrowing-conversion-to-bool-in-list-initialization-strange-behaviour – Destructor Mar 12 '16 at 16:34
  • @zenith: Clang [doesn't issue any warnings](http://coliru.stacked-crooked.com/a/85148936c9436621) for this either. – wally Mar 12 '16 at 16:41
  • I can't think of a way to do that for assignment, short of declaring an extraneous helper variable? Also, it doesn't work for declarations with g++. Or Visual C++. – Cheers and hth. - Alf Mar 12 '16 at 17:10
  • @Cheersandhth.-Alf Simply `x = {f()};` works for me. – Emil Laine Mar 12 '16 at 19:11
  • Only `return {x};` seems to emit warnings on gcc ([live on coliru](http://coliru.stacked-crooked.com/a/5335f26c8341eb2c)) – Emil Laine Mar 12 '16 at 19:14
  • @flatmouse It [does](http://coliru.stacked-crooked.com/a/ed561ca51fe0f43a) if you use the uniform initialization syntax. – Emil Laine Mar 12 '16 at 19:18
  • @zenith I suppose I'm being pedantic, but [so does g++](http://coliru.stacked-crooked.com/a/31a10c36ca5cc000). – wally Mar 13 '16 at 12:57
  • 1
    @flatmouse I'm repeating my comment above, but it does it only on the return statement. – Emil Laine Mar 13 '16 at 13:26
2

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

Kyle Markley
  • 155
  • 4
1

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • The "performance" aspect aside, what's your opinion on readability? Even if that's apparently not the intention of the VC warning, I've often seen it pointing out code which could be made much clearer with an explicit check. For example, `double x = 0.0; bool b = (x != 0.0);` versus `double x = 0.0; bool b = x;`. – Christian Hackl Mar 12 '16 at 17:32
0

In c++, forcing the coder to handle types himself, frees up resources for runtime. The price is keeping track of types, but the benefit is efficency and speed.

Its a small price to pay, but the reward is the ability to do more, in less time.

Charlie
  • 585
  • 9
  • 17
  • "forcing the coder to handle types himself, frees up resources for runtime". C++ is statically typed i.e. all types are checked at compile-time, so there's nothing to free for runtime regarding types. – Emil Laine Mar 12 '16 at 19:41