无法重载 GCC 128 位整数的 std::abs()

Cannot overload std::abs() for GCC 128 bit integer

提问人:intrigued_66 提问时间:12/13/2022 最后编辑:Peter Cordesintrigued_66 更新时间:12/16/2022 访问量:341

问:

当我使用 GCC 的 128 位类型调用时,我遇到编译器错误。我的行看起来像:abs()

__extension__ using int128_t = __int128;

int128_t mantissa_;

// Other code

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

我在本地遇到的错误是:

 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)

所以它不考虑我的超载(下图)作为候选人吗?

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

重载是否正确?

但是,当我在 Godbolt 中模拟一个示例时,即使没有重载,它也能很好地编译:abs()

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

Godbolt 是否使用像 Abseil 这样的库,他们正在提供一个函数,这就是它编译的原因?

C++ 模板 GCC x86 int128

评论

3赞 463035818_is_not_an_ai 12/13/2022
您不能重载 。不允许添加到命名空间(少数例外)std::absstd
1赞 463035818_is_not_an_ai 12/13/2022
为什么要重载它?你为什么不打电话?myabs(__int128)
0赞 Clifford 12/13/2022
你能简化你的例子并完整地发布它吗?此 onlinegdb.com/jlw0sEzKti 在 onlinegdb 中编译。您的声明在使用时是否可见?通过定义或声明在同一个翻译单元中使用之前。显然,如果定义位于单独的翻译单元中,则仅包含 <cmath> 或 <cstdlib> 是不可见的。std::abs(int128_t)extern int128_t std::abs(int128_t);
1赞 Clifford 12/13/2022
...@463035818_is_not_a_number说的 - 即使有效,这也是一个坏主意。
0赞 Clifford 12/13/2022
关于你的godbolt问题,有一个按钮“库”,显示使用了哪些库。绳降可用,但默认情况下未链接。您的示例在 Godbolt 中使用 clang godbolt.org/z/f6EoGnva4 失败。我的例子并没有那么清楚地 godbolt.org/z/fzrboPvK3 可见性的事情。

答:

4赞 Turtlefight 12/13/2022 #1

1. 如何开始工作std::abs__int128

以下内容仅适用于 gcc 版本 >= 7。

对于 gcc,使用以下代码片段提供重载:__int128std::abs

libstdc++-v3/include/bits/std_abs.h

#if defined(__GLIBCXX_TYPE_INT_N_0)
  __extension__ inline _GLIBCXX_CONSTEXPR __GLIBCXX_TYPE_INT_N_0
  abs(__GLIBCXX_TYPE_INT_N_0 __x) { return __x >= 0 ? __x : -__x; }
#endif

(或以下 3 个类似函数之一,使用 、 等)__GLIBCXX_TYPE_INT_N_1__GLIBCXX_TYPE_INT_N_2

要定义这些宏,您需要在启用 gnu 扩展的情况下进行编译(即不是严格的 c++) 为此,您需要使用 而不是 (或 , 等... 取决于您要面向的 C++ 版本) (如果根本没有给出任何选项,则 gcc 将默认为 (c++ 版本,取决于编译器版本))

-std=gnu++20-std=c++20-std=gnu++17-std=gnu++14-std=std=gnu++xx

当使用 gnu 扩展进行编译时,gcc 会自动定义这些宏(假设 gcc 支持目标平台的 128 位 int):
godbolt
__int128

#define __GLIBCXX_BITSIZE_INT_N_0 128
#define __GLIBCXX_TYPE_INT_N_0 __int128

不幸的是,c 函数系列(以及它内置的 gcc - )根本不支持,调用它们会导致结果值被截断。abs__builtin_abs__int128

示例:(编译方式):
godbolt
-std=gnu++20 -Wconversion

#include <cmath>

__extension__ using int128_t = __int128;

int128_t mantissa_;

int main() {
    {
        using namespace std;
        // OK - calls std::abs(__int128)
        int128_t foo = abs(mantissa_);
    }

    // OK - calls std::abs(__int128)
    int128_t bar = std::abs(mantissa_);

    // WARNING: calls abs(int) --> truncation
    int128_t baz = abs(mantissa_);

    // WARNING: calls abs(int) --> truncation
    int128_t foobar = __builtin_abs(mantissa_);
}

2. 为什么 godbolt 编译

您提供的 godbolt 由于调用 c 函数 int abs(int) 而编译(代码不包括 ,因此不可见)
using namespace std;std::abs

c 系列函数对可能的类型具有不同的名称,因此对 with as 参数的调用不会模棱两可(但会导致截断为 ):absabs()__int128int

int        abs  ( int n );
long       labs ( long n );
long long  llabs( long long n );

/* floating-point versions would be fabs, ... */

std::abs 的 c++ 变体是用重载实现的,因此假设 defined 没有重载,对 with 的调用将是模棱两可的。 (、、、等)为积分转换;、、等为浮点积分转换积分转换和浮点积分转换都具有相同的秩 (Conversion),因此这些重载都不会比任何其他 -> 歧义的更匹配)absstd::abs__int128__int128__int128 -> long long__int128 -> long__int128 -> int__int128 -> double__int128 -> float

namespace std {
    int       abs( int n );
    long      abs( long n );
    long long abs( long long n );

    /* more overloads of abs for floating-point types */
}

3. 手动添加函数std::abs

请注意,添加声明通常是未定义的行为(有一些例外),所以我不建议这样做。std

以下是 gcc 版本与您的版本的比较:

// version provided by gcc (when compiling with gnu extensions)
// (macros substituted for better readability)
namespace std {
    __extension__ inline constexpr __int128 abs(__int128 __x) {
        return __x >= 0 ? __x : -__x;
    }
}

// your version
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
    }
}

它们在功能上是相同的,三元条件只是反转,你要乘以负一,而 gcc 使用一元减运算符。

Godbolt 示例

评论

1赞 intrigued_66 12/16/2022
我正在使用,但我认为它被解释为因为我收到警告。然后代码就是然后__builtin_absintwarning: conversion from ‘long int’ to ‘int’ may change value [-Wconversion] 197 | return __builtin_abs(x);T template<class T>T my_abs(T x) constreturn __builtin_abs(x);
1赞 Turtlefight 12/16/2022
@intrigued_66对不起,我搞砸了 - 我不小心阅读了 -builtin 的代码而不是(错过了前面的 f),所以不幸的是,我所说的仅适用于 ,但不适用于 .对于这个错误,我感到非常抱歉,我将在接下来的一个小时内使用正确的信息更新我的帖子。fabsabs__float128__int128
0赞 intrigued_66 12/16/2022
不用担心。只是想......难道没有办法覆盖符号位吗?那一定比CMOV和乘法更快吗?
0赞 Turtlefight 12/20/2022
@intrigued_66不幸的是,对于两个人的补充来说,这并不那么简单。IEEE-754 浮点数确实有一个专用的符号位,因此对于那些简单且可用于将符号位设置为零(godbolt - 注意如何并且是位掩码,除了 1 位(符号位))。对于两个的补充来说,这有点棘手;最高有效位表示数字是正数还是负数,但负值的顺序与正值相反 - 因此简单地翻转最高有效位是行不通的: 示例.LC0.LC1
0赞 Turtlefight 12/20/2022
在 2 的补码中实现的常用方法是反转值并添加 1,即: (这是对为什么会这样的简单解释) - 这也是 gcc 将为你的 abs 实现和它的内置实现生成的(假设你在编译时启用了优化): godbolt(所有 4 个函数都产生相同的指令)absabs(x) == x >= 0 ? x : (~x + 1)