看起来libstdc++
是正确的,这不是病态的,尽管我们会看到有人对这是否是 LWG active issue 的缺陷表示怀疑2192
。
C++11 标准部分草案26.8
[c.math]段落11
说:
此外,应有足够的额外过载以确保:
并包括以下项目:
- 否则,如果对应于 double 参数的任何实参具有 double 类型或整数类型,则对应于 double 形参的所有实参都将有效地强制转换为 double。
我们可以看到这libstdc++
确实提供了这种情况:
template<typename _Tp>
inline typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }
如果 llabs 不存在,还有一个gcc
错误报告std::abs (long long) 诉诸 std::abs (double),它质疑此实现是否正确,一个回复说:
[...]按照标准很好,任何整数都应该无条件地变成双倍。[...]
该错误报告最终导致LWG 活跃问题 2192: Validity and return type of std::abs(0u) isclearing being filed which says in other things:
- 在 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+ 中从 C99/C1x 设置的相应“泛型类型函数”规则3 仅限于 和 的浮点函数,因此不能应用于 abs 函数(但应用于 fabs 函数!)。
问题是这是否也适用于abs
。这可能是一个缺陷,因为似乎没有办法解释当前的措辞 exclude 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)
{
// ...
}
// ...
}