3

设置

我正在使用名为 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<float>(camera.Roll, evaluateInfo)GetScalar<bool>(camera.Visibility, evaluateInfo)代替多行未初始化的缓冲区和乱码乱扔我的代码。

我正在 Visual Studio 中编译/W4并解决所有出现的警告。当我使用GetScalar<bool>时,编译器会产生C4800 警告

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

当编译器创建GetScalar<bool>时,它会以一个从 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<bool>(x)完全等同于x != 0.0. 事实上,在发布模式下编译的简单测试在两种情况下都会给我相同的程序集输出。那么,为什么 C4800 会称自己为“性能警告”?上面列出的两个选项在功能上是否相同?如果归结为风格问题,戴上你最好的学究式帽子后,你更喜欢哪个选项?

4

4 回答 4

4

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

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

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

或者您可以有选择地禁用该警告,但如果您这样做,请在评论中解释为什么这里有必要这样做。

于 2013-07-10T21:13:12.730 回答
4

我不确定您的特定设置的最佳解决方案是什么(我认为静态转换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);

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

于 2013-07-10T21:14:46.057 回答
4

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

于 2013-07-10T21:54:47.843 回答
0

这是我会使用演员表的功能符号版本而不是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

于 2013-07-11T12:18:51.853 回答