从双精度到布尔值的 C++ 隐式转换是危险的



这是我第二次犯大错误,通过创建一个布尔类型的变量而不是双精度。例如,考虑

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 行,这显然是不好的)?

您可以使用统一初始化来获取错误:

bool x{0.0};

错误:在初始值设定项列表中,类型"double"无法缩小为"bool" [-Wc++11-narrowing]

它也可以用于分配:x = {f()}; 和返回return {x};

虽然我没有直接的答案(要求编译器警告),但我确实有一个不透明的 typedef 库,其中包含一个"inconvertibool"类型,它与布尔类似,但不适用于其他类型,如 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 时才有帮助,但它与您的场景不太匹配,因为您说你的意思是"双精度",而不是"bool"。

Visual C++ 编译器警告转换为 bool 的,但有一个愚蠢的性能警告。通常这是一个不受欢迎的警告,但不幸的是,它不能用简单的演员表来静音。沉默它的近乎成语是使用双重否定, !!,砰,例如 return !!0.0 .

你的问题是相反的,你想要这样的警告或错误,但仍然砰几乎成语可以成为解决方案的一部分。

有了下面举例说明的想法,您只需编写Bool而不是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.cppfoo.cpp:在函数 'Bool something()' 中:foo.cpp:43:22:错误:无法将"0.0"从"双精度"转换为"布尔"     { 返回 TO_BOOL 0.0; }                        ΓåÉ 43号线                      ^foo.cpp:在函数'double g()'中:foo.cpp:48:25:错误:请求从"双精度"转换为非标量类型"Bool"        布尔值 x = TO_BOOL 0.0;                       ΓåÉ 48号线                         ^foo.cpp:50:13:错误:与"运算符="不匹配(操作数类型为"布尔"和"双精度")           x = TO_BOOL f(); 其中 f() 是一个返回双精度的函数             ^foo.cpp:23:14:注意:候选:模板<类T,类Enabled_> Bool& Bool::operator=(T)         自动运算符=( T 常量其他 )              ^foo.cpp:23:14:注意:模板参数推导/替换失败:foo.cpp:12:11: 注意:候选人:Bool& Bool::operator=(const 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:警告:建议在"||"中的"&&"两边加上括号[-括号]         返回 A && B ||某物();                  ^c:\my\forums\so\105> g++ foo.cpp -D CLEANfoo.cpp: 在函数 'int main()' 中:foo.cpp:58:18:警告:建议在"||"中的"&&"两边加上括号[-括号]         返回 A && B ||某物();                  ^c:\my\forums\so\105> g++ foo.cpp -D CLEAN -Wno-括号c:\my\forums\so\105> _

如果您希望不考虑从 Boolbool以外的其他类型的隐式转换,只需将该转换运算符也设置为选中的模板,如构造函数和赋值运算符。

在 c++ 中,强制编码人员自己处理类型,为运行时释放资源。价格是跟踪类型,但好处是效率和速度。

这是一个很小的代价,但回报是能够在更短的时间内做更多的事情。

最新更新