Home > Back-end >  Cannot overload std::abs() for GCC 128 bit integer
Cannot overload std::abs() for GCC 128 bit integer

Time:12-13

I'm getting compiler errors when I call abs() with GCC's 128 bit type. My line looks like:

__extension__ using int128_t = __int128;

int128_t mantissa_;

// Other code

using namespace std;
int128_t temp = abs(mantissa_);

The error I'm getting locally is:

 error: call of overloaded ‘abs(const int128_t&)’ is ambiguous
     int128_t temp = abs(mantissa_);
/usr/include/stdlib.h:840:12: note: candidate: ‘int abs(int)’
  840 | extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;

/usr/include/c  /11/bits/std_abs.h:56:3: note: candidate: ‘long int std::abs(long int)’
   56 |   abs(long __i) { return __builtin_labs(__i); }
      |   ^~~
/usr/include/c  /11/bits/std_abs.h:61:3: note: candidate: ‘long long int std::abs(long long int)’
   61 |   abs(long long __x) { return __builtin_llabs (__x); }
      |   ^~~
/usr/include/c  /11/bits/std_abs.h:71:3: note: candidate: ‘constexpr double std::abs(double)’
   71 |   abs(double __x)
      |   ^~~
/usr/include/c  /11/bits/std_abs.h:75:3: note: candidate: ‘constexpr float std::abs(float)’
   75 |   abs(float __x)
      |   ^~~
/usr/include/c  /11/bits/std_abs.h:79:3: note: candidate: ‘constexpr long double std::abs(long double)’
   79 |   abs(long double __x)

so it's not considering my overload (below) as a candidate?

namespace std 
{
    __extension__ using int128_t = __int128;
    
    int128_t abs(int128_t x)
    {
        return x < 0 ? x * -1 : x;  // Not ideal but no builtin for 128
    }
}

Is the overload correct?

However, when I mock an example in Godbolt, even without overloading abs(), it compiles fine:

https://godbolt.org/z/P8T1fGxcK

#include <iostream>

__extension__ using int128_t = __int128;

struct Decimal
{
    Decimal(int128_t q) : mantissa_(q)
    {
        volatile int128_t x = abs(mantissa_);  // How is this compiling?
        std::cout << "ctor" << std::endl;
    }

    int128_t mantissa_;
};

int main()
{
    Decimal dec(-6);
}

Are Godbolt using a library like Abseil, they're providing a function and that's why it is compiling?

CodePudding user response:

tl;dr

Remove using namespace std; or call the builtin directly (__int128 temp = __builtin_abs(mantissa_);)


Note that __int128 is a gcc-specific compiler extension (so non-portable code)

Why the godbolt works

gcc will automatically treat a set of common c functions as builtins:

6.59 Other Built-in Functions Provided by GCC

The ISO C90 functions abort, abs, acos, [...], vprintf and vsprintf are all recognized as built-in functions unless -fno-builtin is specified (or -fno-builtin-function is specified for an individual function). All of these functions have corresponding versions prefixed with __builtin_.

So when you compile a program like the following the call to abs will automatically be interpreted as __builtin_abs:
godbolt

int main() {
  __int128 foo = 1;
  // equivalent to  __int128 absFoo = __builtin_abs(foo);
  __int128 absFoo = abs(foo);
}

The builtin variants of those functions do support __int128, therefore this works.

Note that this builtin-transformation ONLY works in case overload resolution determines that abs(foo) would call the c function abs and ONLY if you're compiling without -fno-builtin.

i.e. the c variant of abs - std::abs - will NOT be transformed into its builtin-equivalent and therefore this will not compile (there is no overload for _int128):
godbolt

int main()
{
    __int128 foo = 1;
    // ambigous function call - there is no overload for __int128
    __int128 absFoo = std::abs(foo);
}

Why your code example doesn't work

Your code example contains using namespace std; - this will bring std::abs into scope which will prevent the abs(mantissa_) call from being treated as a builtin (and due to std::abs having no overload for __int128 you'll get an ambigous function call error)

To make this work you basically have 2 options:

  • Don't use using namespace std; and rely on the builtin-transformation gcc provides, e.g.:
    /*using namespace std;*/
    int128_t temp = abs(mantissa_);
    
  • Explicitly call the builtin you want to use:
    int128_t temp = __builtin_abs(mantissa);
    
  • Related