鉴于以下计划:
#include <cmath>
int main()
{
std::abs(0u) ;
}
gcc
和clang
不同意这是否是不正确的。使用gcc
和libstdc++
代码构建时没有错误或警告( see it live ),而clang
与libc++
一起使用会生成以下错误( see it live ):
error: call to 'abs' is ambiguous
std::abs(0u) ;
^~~~~~~~
哪个结果是正确的? abs(0u)
是否应该含糊不清?
MSalters指出了一个有趣的相关问题:Template version of std::abs。
答案 0 :(得分:16)
看起来libstdc++
是正确的,这不是格式错误,但我们会看到对于这是否是LWG活动问题2192
中的缺陷存在一些疑问。
草案C ++ 11标准部分26.8
[c.math] 段11
说:
此外,还应有足够的额外过载来确保:
并包含以下项目:
- 否则,如果对应于double参数的任何参数具有double类型或整数类型,则所有参数都对应于 双参数有效地加倍。
醇>
我们可以看到libstdc++
indeed provide for this案例:
template<typename _Tp>
inline typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }
还有一个gcc
错误报告std::abs (long long) resorts to std::abs (double) if llabs is absent,该报告会询问此实施是否正确,并且有一条回复说明:
[...]根据标准是好的,任何整数应该是 无条件地成为双倍。 [...]
错误报告最终导致LWG active issue 2192: Validity and return type of std::abs(0u) is unclear被提交,其中包括:
- 在C ++ 11中,额外的&#34;充分的过载&#34;规则从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(&#34; [..]或者 26.8 [c.math] p11的整数类型[..]&#34;)(注意当前 LWG 2086的解决方案无法解决这个问题。)
- 由于返回类型的两个冲突要求,任何包含两者的翻译单元都可能格式不正确 重载std :: abs(int)。
醇>在我看来,至少第二个结果并非意图, 我个人认为两者都是不幸的[...]它也应该是 注意到,相应的&#34;泛型类型函数&#34;规则集 7.25 p2 + 3中的C99 / C1x仅限于浮点函数 来自和,所以不能适用于绝对 功能(但对于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) { // ... } // ... }