visual c++ std::isfinite() 是否符合标准?

Is visual c++ std::isfinite() standard conforming?

我得到了一个包装器 class,它有一个简单的 light-weighted 隐式转换运算符到 double

我喜欢像使用 double 一样使用它,例如:

if (!std::isfinite(myVar)) ...

但是std::isfinite(double)的visual c++实现实际上是一个模板,通过复制得到他的参数。

所以我的包装器 class 复制构造函数被调用,它不是 light-weighted.

为了避免这种情况,我必须写:

if (!std::isfinite((double)myVar)) ...

对于每次调用 :(

如果 visual c++ std::isfinite() 的定义与 cppreference.com 上的一样,我就不必每次都调用:([edit] 我可能错了,Integral 不是实际类型...但是...[编辑]它仍然不应该接受用户定义的类型?)

bool isfinite( float arg );
bool isfinite( double arg );
bool isfinite( long double arg );
bool isfinite( Integral arg );

我不确定标准对此有何规定。 是 vc++ 模板 std::isfinite standard-conforming ?

我应该将此报告为 Microsoft 连接上的错误吗?

我应该定义自己的 isfinite(double) 来调用 std::isfinite 吗?

编辑

或者它可能是一个 non-issue,因为在发布版本中,调用被内联并且没有发生复制? (好吧,我会尝试立即检查并在几分钟内更新)

编辑 2

它似乎没有在带有 /Ob2 的发布版本中内联(内联任何合适的函数)

编辑 3

根据要求,样品:

struct DoubleWrapper {

  double value;

  DoubleWrapper(double value) : value(value) {
    printf("copy Ctor\n");
  }

  DoubleWrapper(const DoubleWrapper & that) : value(that.value) {}

  operator double() const {
    return this->value;
  }
};

int main() {

  DoubleWrapper a(rand());      //rand to prevent optimization

  auto res = std::isfinite(a);

  printf("%d", res);            //printf to prevent optimization
}

编辑 4

因此,根据 Ben Voigt 的评论,这就是我添加到 class header:

中的内容
#include <cmath>

namespace std {
    inline bool isfinite(const DoubleWrapper<double> & dw) {
        return isfinite((double)dw);
    }
}

这是正确的解决方案吗?

唯一的问题是,我是否应该对所有需要 double 的 <cmath> 函数做同样的事情?

编辑 5

我在这里回复 Shafik Yaghmour 的回答,因为评论太有限(也许我应该开始一个新问题)

如果我理解正确,而不是我的编辑 4,我应该将它添加到我的 class header:

inline bool isfinite(const DoubleWrapper<double> & dw) {
    return isfinite((double)dw);
}

using std::isfinite;

是否需要将它放在一个命名空间中,或者我可以将它留在 "global" 命名空间中吗?

但这意味着我必须将所有调用从 std::isfinite(dw) 更改为 isfinite(dw)

好的,但我发现有很多事情我不明白。 我很困惑。

我知道向 std 添加 overload 是不允许的。

但是,允许添加模板专业化?为什么 ?它有什么区别?

无论如何,我试过了,但它不能解决我的问题,因为这个专业化:

template<> inline __nothrow bool std::isfinite(const MagicTarget<double> & mt) {
    return std::isfinite((double)mt);
}

编译器不会选择超过标准的:

template<class _Ty> inline __nothrow bool isfinite(_Ty _X)
{
    return (fpclassify(_X) <= 0);
}

要被选中,应该是

template<> inline __nothrow bool std::isfinite(MagicTarget<double> mt) {
    return std::isfinite((double)mt);
}

但这仍然会调用副本 Ctor :(

令人惊讶的是,在标准模板上选择了重载(见编辑 4)... 我开始认为某些 C++ 规则对我来说太微妙了:(

但是,首先,为什么 cmath 函数,尤其是 std::isfinite 是模板?

接受任何其他浮点类型有什么意义?

无论如何,vc++ std::isfinite 调用仅为 float、double 和 long double 定义的 std::fpclassify。 所以...有什么意义?

我认为标准委员会通过允许 cmath 函数作为模板搞砸了。它们应该只为相关的类型定义,或者可能将它们的参数作为通用引用。

就是这样,不好意思再吐槽了...

我会去(不是在 std 中)过载。 谢谢!

解决您的问题的编辑 4,因为通过评论您已经接近最终解决方案。

您不应该将它添加到 std 命名空间,您应该将它添加到您自己的命名空间之一,rely on argument dependent lookup see Is it a good practice to overload math functions in namespace std in c++ 了解更多详细信息。

鉴于标准委员会明显希望限制 cmath 函数只能用算术类型调用,依赖隐式转换不是一个好主意。因此,通过强制转换执行显式转换是所有 cmath 函数的安全方法:

isfinite((double)dw)

您可以在 中找到详细信息。