Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous call to abs

I have a custom data type that in practice can be either float or double. On every OS except OSX, I am able to successfully build this C++11 template:

#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;
}

However, clang 6.0 (3.5 LLVM) reports an ambiguous function call. If I change abs to fabs, the error is resolved on OSX, but now an identical error shows up on my Linux clang, gcc, and Visual Studio.

Error on Visual Studio with fabs:

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

UPDATE

This example compiled on our OS X systems, although in the nearly identical project it does not. The solution was including <cstdlib> explicitly in the source, rather than back in another header. The reason is unclear, but seems to be xcode/clang not following our header includes properly.

like image 877
EntangledLoops Avatar asked May 06 '15 18:05

EntangledLoops


People also ask

How do you fix ambiguous call of overloading?

There are two ways to resolve this ambiguity: Typecast char to float. Remove either one of the ambiguity generating functions float or double and add overloaded function with an int type parameter.

How do you find absolute value in C++?

The abs() in C++ returns the absolute value of an integer number. If the number is negative, it will return the positive value (with the same magnitude), for example, abs(-1) = 1. If the number is already positive or zero, it will return the number as it is, for example, abs(1) = 1.


2 Answers

The issue is that libc++ is not entirely C++11 compliant with the integral overload for std::abs in cmath:

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

Including cstdlib solves your problem since that header has overloads specifically for integer types.

For reference the draft C++11 standard section 26.8 [c.math] paragraph 11 says:

Moreover, there shall be additional overloads sufficient to ensure:

and includes the following item:

  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.

This is situation very likely to change due to LWG active issue 2192: Validity and return type of std::abs(0u) is unclear. I am guessing libc++ choose not to provide the overloads in cmath due to the issue brought up in this defect report.

See Is std::abs(0u) ill-formed? for more details on this.

like image 139
Shafik Yaghmour Avatar answered Sep 30 '22 15:09

Shafik Yaghmour


If you have many template functions causing this problem, you can use the following drop-in replacement:

#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

Just replace the std::abs calls with util::abs. (Needs c++11.)

like image 41
jan.sende Avatar answered Sep 30 '22 15:09

jan.sende