提问人:intrigued_66 提问时间:12/13/2022 最后编辑:Peter Cordesintrigued_66 更新时间:12/16/2022 访问量:341
无法重载 GCC 128 位整数的 std::abs()
Cannot overload std::abs() for GCC 128 bit integer
问:
当我使用 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 这样的库,他们正在提供一个函数,这就是它编译的原因?
答:
1. 如何开始工作std::abs
__int128
以下内容仅适用于 gcc 版本 >= 7。
对于 gcc,使用以下代码片段提供重载:__int128
std::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 参数的调用不会模棱两可(但会导致截断为 ):abs
abs()
__int128
int
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),因此这些重载都不会比任何其他 -> 歧义的更匹配)abs
std::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 使用一元减运算符。
评论
__builtin_abs
int
warning: conversion from ‘long int’ to ‘int’ may change value [-Wconversion] 197 | return __builtin_abs(x);
T template<class T>
T my_abs(T x) const
return __builtin_abs(x);
fabs
abs
__float128
__int128
评论
std::abs
std
myabs(__int128)
std::abs(int128_t)
extern int128_t std::abs(int128_t);