对 abs 的模糊调用

Ambiguous call to abs

我有一个自定义数据类型,实际上可以是 floatdouble。在除 OSX 之外的每个 OS 上,我都能够成功构建此 C++11 模板:

#include <cmath>
#include <cstdlib>
#include <cstdint>

template< class REAL_T >
inline REAL_T inhouse_abs(REAL_T i_val)
{
    return std::abs((REAL_T)i_val);
}

int main()
{
    int32_t ui = 2;
    inhouse_abs(ui);
    return 0;
}

但是,clang 6.0 (3.5 LLVM) 报告了一个不明确的函数调用。如果我将 abs 更改为 fabs,错误会在 OSX 上得到解决,但现在在我的 Linux clang、gcc 和 [=32= 上会出现相同的错误].

晶圆厂 Visual Studio 出错:

349 error C2668: 'fabs' : ambiguous call to overloaded function

更新

此示例在我们的 OS X 系统上编译,尽管在 几乎 相同的项目中它没有。解决方案是在源代码中明确包含 <cstdlib>,而不是在另一个 header 中返回。原因尚不清楚,但似乎是 xcode/clang 没有正确遵循我们的 header 包含。

问题是 libc++ 不完全符合 std::abs in cmath 的积分重载的 C++11:

double      fabs( Integral arg ); (7)   (since C++11)

包含 cstdlib 可以解决您的问题,因为 header 具有专门针对整数类型的重载。

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

Moreover, there shall be additional overloads sufficient to ensure:

并包括以下项目:

  1. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.

这种情况很可能会因 LWG active issue 2192: Validity and return type of std::abs(0u) is unclear 而改变。我猜 libc++ 选择不在 cmath 中提供重载是由于此缺陷报告中提出的问题。

有关详细信息,请参阅 Is std::abs(0u) ill-formed?

解决方案是在 OS X 机器上显式 #include <cstdlib>,因为某些原因 Visual Studio 在我们的依赖项中找到它并包含它,但 clang 没有。我将尝试重现我们的项目具有的类似链,并以极简主义的方式重现错误,因为它可能仍然是 Xcode 或 Visual Studio.

的问题

如果您有很多模板函数导致此问题,您可以使用以下直接替换:

#include <cmath>
#include <cstdlib>
#include <type_traits>

namespace util {


template <class T>
auto abs(T value) -> std::enable_if_t<std::is_unsigned<T>::value,
                                      T> { return value; }
template <class T>
auto abs(T value) -> std::enable_if_t<std::is_floating_point<T>::value,
                                      T> { return std::fabs(value); }
template <class T>
auto abs(T value) -> std::enable_if_t<std::is_same<T, int>::value,
                                      T> { return std::abs(value); }
template <class T>
auto abs(T value) -> std::enable_if_t<std::is_same<T, long>::value,
                                      T> { return std::labs(value); }
template <class T>
auto abs(T value) -> std::enable_if_t<std::is_same<T, long long>::value,
                                      T> { return std::llabs(value); }
template <class T>
auto abs(T value) -> std::enable_if_t<std::is_signed<T>::value &&
                                          !std::is_floating_point<T>::value &&
                                          !std::is_same<T, int>::value &&
                                          !std::is_same<T, long>::value &&
                                          !std::is_same<T, long long>::value,
                                      T> { return std::abs(value); }


} // namespace util

只需将 std::abs 调用替换为 util::abs。 (需要 c++11。)