【问题标题】:Is there a difference between `static_cast<bool>(x)` and `x != 0.0`?`static_cast<bool>(x)` 和 `x != 0.0` 之间有区别吗?
【发布时间】:2013-07-10 21:06:57
【问题描述】:

设置

我正在为一个名为 MotionBuilder 的应用程序使用 API。为了访问 MotionBuilder 属性的值,您将其读入一个双精度变量,而不管它实际代表的是哪种数据类型。

这是我编写的一个实用函数,用于评估标量属性的值:

template <typename DataT>
inline DataT GetScalar(FBProperty& prop, FBEvaluateInfo* evaluateInfo)
{
    double data = 0.0;
    prop.GetData(&data, sizeof(data), evaluateInfo);
    return static_cast<DataT>(data);
}

这样,我可以编写GetScalar&lt;float&gt;(camera.Roll, evaluateInfo)GetScalar&lt;bool&gt;(camera.Visibility, evaluateInfo),而不是让未初始化的缓冲区和强制转换乱七八糟的多行乱七八糟的代码。

我正在使用 /W4 在 Visual Studio 中进行编译,并在出现所有警告时对其进行处理。当我使用GetScalar&lt;bool&gt; 时,编译器会生成a C4800 warning

'double' : forcing value to bool 'true' or 'false' (performance warning)

当编译器创建GetScalar&lt;bool&gt; 时,它会以一个从double 到bool 的static_cast 结束,这显然是它不喜欢的。由于我最初的目标是使用单个模板函数处理多种类型(bool、float、double 等),因此我不能只添加通常的 != 0.0

为了解决这个警告,我有两个选择。

选项 1

我可以直接使用编译指示来抑制警告,因为演员正在做我想让它做的事情:

template <typename DataT>
inline DataT GetScalar(FBProperty& prop, FBEvaluateInfo* evaluateInfo)
{
    double data = 0.0;
    prop.GetData(&data, sizeof(data), evaluateInfo);
#pragma warning (push)
#pragma warning (disable: 4800) // Don't complain about casting to bool
    return static_cast<DataT>(data);
#pragma warning (pop)
}

选项 2

我可以添加GetScalar 的特化来处理bool 案例:

template <>
inline bool GetScalar<bool>(FBProperty& prop, FBEvaluateInfo* evaluateInfo)
{
    double data = 0.0;
    prop.GetData(&data, sizeof(data), evaluateInfo);
    return data != 0.0;
}

问题

我认为对于某些双 x,static_cast&lt;bool&gt;(x) 完全等同于 x != 0.0。事实上,在发布模式下编译的简单测试在两种情况下都会给我相同的程序集输出。那么,为什么 C4800 会称自己为“性能警告”呢?上面列出的两个选项在功能上是否相同?如果归结为风格问题,戴上你最好的学究式帽子后,你更喜欢哪个选项?

【问题讨论】:

  • 另一个选项是!!x,它返回bool

标签: c++ templates casting warnings


【解决方案1】:

这是一个警告,它告诉您此转换可能存在性能问题。既然你想做转换,那就去做吧。不要浪费时间为没有告诉您任何有用的警告编写详细的解决方法。

【讨论】:

    【解决方案2】:

    我确实认为对于 bool 的情况,与 0 的比较比强制转换更具可读性。我还会质疑拥有一个可以专门用于数字类型的函数和具有相同实现的 bool 在语义上是否有意义,即使这恰好在这里工作。

    一般。在你的特殊情况下,我认为有一个统一的模板很好,如果它避免了代码重复,那是一个优势。如果有的话,我可能只会为 cast-to-DataT 部分创建一个函数特化,而不是整个函数:

    template <typename T>
    T convertTo(double d) { return static_cast<T>(d); }
    
    template <>
    double convertTo<bool>(double d) { return d != 0.0; }
    
    template <typename DataT>
    inline DataT GetScalar(FBProperty& prop, FBEvaluateInfo* evaluateInfo)
    {
        double data = 0.0;
        prop.GetData(&data, sizeof(data), evaluateInfo);
        return convertTo<DataT>(data);
    }
    

    或者您可以选择性地禁用警告,但如果您这样做,请在评论中解释为什么在此处必须这样做。

    【讨论】:

      【解决方案3】:

      我不确定对于您的特定设置的最佳解决方案是什么(我认为静态转换为 bool 非常好),但处理分支的通用方法是为每个单独的操作创建单独的特征。在你的情况下,这是“convert_to_bool”:

      template <typename Out>
      struct converter
      {
          static Out from_double(double x)
          {
              return static_cast<Out>(x);
          }
      };
      
      template <>
      struct converter<bool>
      {
          static bool from_double(double x)
          {
              return x != 0;
          }
      };
      

      现在你可以使用了:

      return converter<DataT>::from_double(data);
      

      通过这种方式,您可以处理主模板中出现的每种情况,并且需要对某些类型进行特殊处理,而无需对主模板进行分支。

      【讨论】:

        【解决方案4】:

        这是我会使用演员表的功能符号版本而不是static_cast 的罕见情况之一。编译器不会给出任何警告,因为这种强制转换形式有效地告诉它你“知道你在做什么”。

        template <typename DataT>
        inline DataT GetScalar(FBProperty& prop, FBEvaluateInfo* evaluateInfo)
        {
            double data = 0.0;
            prop.GetData(&data, sizeof(data), evaluateInfo);
            return DataT(data);
        }
        

        一般来说,请注意在 C++ 中使用这种类型的强制转换,因为它会抑制有意义的警告。当我不关心处理溢出时,我只会在算术类型(例如intdoubleenum)之间转换时使用它来抑制警告。

        【讨论】:

          猜你喜欢
          • 2023-03-27
          • 2011-01-14
          • 1970-01-01
          • 1970-01-01
          • 2020-02-28
          • 1970-01-01
          • 2017-01-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多