17

给定以下程序:

#include <cmath>

int main()
{
    std::abs(0u) ;
}

gcc并且clang不同意这是否是错误的。gcc与代码一起使用libstdc++不会出现错误或警告(实时查看),而clanglibc++它一起使用会生成以下错误(实时查看):

error: call to 'abs' is ambiguous
std::abs(0u) ;
^~~~~~~~

哪个结果是正确的?是否应该abs(0u)模棱两可?


MSalters 指出了一个有趣的相关问题:模板版本的 std::abs

4

1 回答 1

17

看起来libstdc++是正确的,这不是病态的,尽管我们会看到有人对这是否是 LWG active issue 的缺陷表示怀疑2192

C++11 标准部分草案26.8 [c.math]段落11说:

此外,应有足够的额外过载以确保:

并包括以下项目:

  1. 否则,如果对应于 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:

  1. 在 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 的当前分辨率不解决这个问题)。

  1. 由于对重载 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)
  {
    // ...
  }
  // ...
}
于 2015-04-20T14:39:55.233 回答