TL;DR
根据标准,将非算术类型作为参数传递给cmath
功能正常但有缺陷报告2068
认为最初的意图是cmath
函数应仅限于算术类型,并且使用非算术参数似乎最终可能会导致格式错误。因此,尽管从技术上来说,使用非算术类型作为参数是有效的,但根据缺陷报告,这似乎是有问题的2068
.
Details
The cmath http://en.cppreference.com/w/cpp/numeric/math标准草案部分涵盖了标头26.8
[c.math]提供了额外的float and 长双中定义的每个函数的重载math.h http://en.cppreference.com/w/c/numeric/math这需要一个double论证和进一步的段落11
为足够的过载并说:
此外,还应有足够的额外过载,以确保:
- 如果与 double 参数对应的任何参数的类型为 long double,则与 double 参数对应的所有参数都是
有效地转换为长双倍。
- 否则,如果对应于 double 参数的任何参数具有 double 类型或整数类型,则对应于的所有参数
double 参数被有效地转换为 double。
- 否则,与双参数相对应的所有参数都将有效地转换为浮点型。
这在 C++11 中似乎有效
在 C++11 部分26.8
[c.math]不包括任何不允许非算术参数的限制cmath
功能。在问题的每种情况下,我们都有一个可用的重载,需要double参数,这些应该通过选择过载解析 http://en.cppreference.com/w/cpp/language/overload_resolution.
缺陷报告2086
但对于 C++14 我们有缺陷报告 2086:对数学函数的过于通用的类型支持 http://cplusplus.github.io/LWG/lwg-defects.html#2086,它认为该节的初衷26.8
[c.math]是为了限制cmath
函数仅适用于算术类型,这会模仿他们的工作方式C:
我的印象是这个规则集可能更通用
有意的,我的假设是它是为了模仿 C99/C1x 而编写的
7.25 p2+3 中以“C++”方式设置的规则 [...](请注意,C 约束
C++ 描述为算术类型的有效类型集,但请参阅
下面是一个重要的区别)[...]
并说:
我目前解决这些问题的建议是限制
这些函数的有效参数类型为算术类型。
和改写的部分26.8
段落11
说(强调我的):
此外,还应有足够的额外过载,以确保:
- If any 算术对应于 double 形参的实参的类型为 long double,那么所有算术参数对应于
double 参数有效地转换为 long double。
- 否则,如果有的话算术对应于 double 参数的 argument 类型为 double 或整数类型,则所有算术对应于双参数的参数被有效地转换为
双倍的。
- Otherwise, all arithmetic arguments corresponding to double parameters are effectively cast to
are effectively cast to have type float.
那么这在 C++14 中是无效的吗?
好吧,尽管有这样的意图,但正如讨论中的评论中所述,它在技术上看起来仍然有效libc++ 错误报告:isnan 和类似函数的错误实现 https://llvm.org/bugs/show_bug.cgi?id=18218#c8:
这可能是意图,但我看不到任何方式来阅读
标准的措辞就是这样。从 comment#0 中的示例来看:
std::isnan(A());
没有算术类型的参数,因此没有任何项目符号
26.8/11 适用。重载集包含'isnan(float)'、'isnan(double)'和'isnan(long double)',并且'isnan(float)'应该
被选中。
因此,改写为DR 2086
段落的11不会使调用格式不正确float, double and 长双否则可使用非算术参数重载。
技术上有效,但使用起来有问题
所以虽然C++11和C++14标准不限制cmath
算术参数的函数DR 2068
争论的意图26.8
段落11
是为了限制cmath
函数只接受算术参数,显然是为了弥补 C++14 中的漏洞,但没有提供足够强的限制。
依赖一个在标准的未来版本中可能会变得不正确的功能似乎是有问题的。由于我们存在实现分歧,因此任何依赖于将非算术参数传递给的代码cmath
这些情况下的功能是不可移植的,因此仅在有限的情况下有用。我们有一个替代解决方案,即显式地将非算术类型转换为算术类型,这绕过了整个问题,我们不再需要担心代码格式错误,而且它是可移植的:
std::isgreater( static_cast<double>(s) ,1.0)
^^^^^^^^^^^^^^^^^^^^^^
正如 Potatoswatter 指出的那样,使用一元+
也是一个选项:
std::isgreater( +s ,1.0)
Update
作为 T.C.在 C++11 中指出,可以说26.8
段落11
bullet 3
适用,因为参数既不是长双, double也不是整数,因此应该是类型的参数S
应该投射到float第一的。注意,如缺陷报告所示gcc
从未实施过这个,据我所知也没有实施过clang
.