C++中将double隐式转换为bool类型是危险的。

7

这是我第二次犯大错,本应该创建一个 double 类型的变量却创建了 bool 类型的变量。例如:

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

我认为编译器应该告诉我这是不好的,但我的编译器(g ++)在使用 -Wall 时没有发出小警告……这会导致后来测试中出现错误。 是否有gcc / g ++的某些选项可以发出警告(例如在第1行,显然是不好的)?


你尝试过使用 -pedantic-errors 吗? - Violet Giraffe
我刚刚尝试了:g++ -Wall -Wextra -pedantic-errors -c dummy.cpp,但我仍然没有警告。 - Renaud
MSVC 14 对此确实会生成 C4800 - wally
哎呀,使用g++编译器在这种情况下没有任何警告。:( - wally
而且clang也没有任何抱怨。 - wally
显示剩余2条评论
4个回答

3
您可以使用统一初始化来获取错误:
bool x{0.0};

错误:在初始化列表中,无法将类型“double”缩小为“bool”[-Wc ++ 11-narrowing]

它也可用于赋值:x = {f()};和返回return {x};


我在使用mingw-w64和gcc-5.2,bool x{0.0}没有警告。 - Renaud
1
@Renaud 由于某些原因,gcc没有实现这种行为。如果您想使用此功能,我建议使用clang。 - emlai
@Renaud:这似乎是GCC的一个bug。http://melpon.org/wandbox/permlink/5vqO6rRq8x8sNiix - Destructor
1
@Renaud:也看看这个:https://dev59.com/8F4c5IYBdhLWcg3wzs-Q - Destructor
1
@flatmouse 我在重复我的上面的评论,但它只在返回语句上执行。 - emlai
显示剩余7条评论

2

虽然我没有直接回答(被问到了编译器警告),但是我有一个不透明typedef库,其中包含一个“inconvertibool”类型,它像bool一样工作,但不与int或double等其他类型一起使用。 它可以在编译时对你的示例中的情况进行错误检查:

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
         ^

当然,如果您一直使用这种类型而不是bool类型,这将有所帮助,但它并不完全符合您的情况,因为您说您意思是“double”而不是“bool”。

1
Visual C++编译器会警告转换为bool,但是带有愚蠢的性能警告。通常这是不需要的警告,但不幸的是它不能通过简单的强制转换来消除。几乎惯用语法来消除它是使用双重否定,!!,bang-bang,例如return !!0.0
你的问题相反,你希望出现这样的警告或错误,但仍然可以使用bang-bang几乎惯用语法的一部分来解决问题。
通过以下示例所示的思路,您只需在需要布尔值的位置写入Bool,并使用!!确保清晰的bool值,否则您将得到编译错误。
这个好处是您很可能只需全局搜索和替换代码,将bool替换为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();
}

使用g++编译的示例:

c:\my\forums\so\105> g++ foo.cpp
foo.cpp: 在函数 'Bool something()' 中:
foo.cpp:43:22: 错误:无法将'0.0'从'double'转换为'Bool'
     { return TO_BOOL 0.0; }                         // ← Line 43
                      ^
foo.cpp: 在函数 'double g()' 中:
foo.cpp:48:25: 错误:请求将'double'转换为非标量类型'Bool'
        Bool x = TO_BOOL 0.0;                        // ← Line 48
                         ^
foo.cpp:50:13: 错误:没有匹配的运算符'='(操作数类型是'Bool'和'double')
           x = TO_BOOL f();  // where f() is a function returning a double
             ^
foo.cpp:23:14: 注:候选函数模板<class T, class Enabled_> Bool& Bool::operator=(T)
         auto operator=( T const other )
              ^
foo.cpp:23:14: 注:   模板参数推断/替换失败:
foo.cpp:12:11: 注:候选:Bool& Bool::operator=(const Bool&)
     class Bool
           ^
foo.cpp:12:11: 注:   没有将第1个参数从'double'转换为'const Bool&'
foo.cpp:12:11: 注:候选:Bool& Bool::operator=(Bool&&)
foo.cpp:12:11: 注:   没有将第1个参数从'double'转换为'Bool&&'
foo.cpp: 在函数 'int main()' 中:
foo.cpp:58:18: 警告:建议在'||'内加上'&&'的括号 [-Wparentheses]
         return a && b || something();
                  ^
c:\my\forums\so\105> g++ foo.cpp -D CLEAN foo.cpp: 在函数 'int main()' 中: foo.cpp:58:18: 警告:建议在'||'内加上'&&'的括号 [-Wparentheses] return a && b || something(); ^
c:\my\forums\so\105> g++ foo.cpp -D CLEAN -Wno-parentheses c:\my\forums\so\105> _

如果您希望将Bool隐式转换为除bool以外的其他类型不被考虑,则还需将该转换运算符作为已检查的模板进行转换,就像构造函数和赋值运算符一样。


除了“性能”方面,您对可读性有什么看法?即使这显然不是 VC 警告的意图,我经常看到它指出可以通过显式检查使代码更清晰的代码。例如,double x = 0.0; bool b = (x != 0.0);double x = 0.0; bool b = x; - Christian Hackl

0
在C++中,强制程序员自己处理类型可以释放运行时的资源。代价是要跟踪类型,但好处是效率和速度。
这只是一个小代价,但回报是能够在更短的时间内完成更多的工作。

强制程序员自己处理类型,为运行时释放资源。C++是静态类型的,即所有类型在编译时都会进行检查,因此在运行时没有关于类型的需要释放的内容。 - emlai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接