好像libstdc++
是正确的,这并不是格式错误的,尽管我们会看到有人对这是否是 LWG 活跃问题中的缺陷表示怀疑2192
.
C++11 标准部分草案26.8
[c.math]段落11
says:
此外,还应有足够的额外过载以确保:
并包括以下项目:
- 否则,如果对应于 double 参数的任何参数具有 double 类型或整数类型,则对应于的所有参数
double 参数被有效地转换为 double。
我们可以看到这个libstdc++
does 确实为此提供了 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/c_std/cmath#L92 case:
template<typename _Tp>
inline typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }
还有一个还有一个gcc
错误报告如果 llabs 不存在,则 std::abs (long long) 诉诸 std::abs (double) https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54686,其中质疑此实施是否正确,一个回应说:
[...]符合标准,任何整数都应该
无条件变成双倍。 [...]
该错误报告最终导致LWG 活动问题 2192:std::abs(0u) 的有效性和返回类型不清楚 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2192提交的文件中除其他外还写着:
- 在 C++11 中,来自 26.8 [c.math] 的附加“足够重载”规则
p11(另请参阅 LWG 2086)可以解读为适用于 std::abs()
过载也是如此,这可能导致以下可能的情况
结论:
该程序
#include <type_traits>
#include <cmath>
static_assert(std::is_same<decltype(std::abs(0u)), double>(), "Oops");
int main() {
std::abs(0u); // Calls std::abs(double)
}
要求格式良好,因为子项目 2(“[..] 或
26.8 [c.math] p11 的整数类型 [..]")(请注意,当前
LWG 2086 的分辨率并没有解决这个问题)。
- 由于返回类型的两个相互冲突的要求,任何包含两者的翻译单元都可能格式错误
重载 std::abs(int) 的。
在我看来,至少第二个结果不是有意的,
我个人认为两者都是不幸的[...]也应该是
注意到,相应的“通用类型函数”规则集来自
7.25 p2+3 中的 C99/C1x 仅限于浮点函数
来自 和 ,因此不能应用于 abs
函数(但针对 fabs 函数!)。
问题是这是否适用于abs
以及。这可能是一个缺陷,因为似乎没有办法解释当前的措辞以排除abs
.
所以目前的措辞表明libstdc++
是一致的,不清楚为什么libc++
按原样选择了当前的实现。我找不到涉及此主题的错误报告或讨论,并且 LWG 问题没有提及不同的实现。
拟议的解决方案将使std::abs(0u)
格式错误:
如果使用无符号整数类型的参数调用abs()
无法通过积分提升 ([conv.prom]) 转换为 int,
程序格式不正确。 [注意:可以提升为 int 的参数
允许与 C 兼容。 — 尾注]
虽然有些人可能会质疑使用的概念abs
对于无符号类型,Howard Hinnant 在报告中指出,使用模板时,这种后果可能并不明显,并提供了一个示例:
[...]特别是在 C++ 中,我们有模板和涉及的类型
在设计时对于程序员来说并不总是显而易见的。例如,
考虑:
template <class Int>
Int
analyze(Int x, Int y)
{
// ...
if (std::abs(x - y) < threshold)
{
// ...
}
// ...
}