【发布时间】:2021-05-09 04:34:21
【问题描述】:
我在一个 C++ 项目中工作,该项目使用了很多类型为 __int128_t 和 __uint128_t 的变量(以下为 int128 和 uint128 以表示敬意)。为了使“cout 调试”更容易,我们为 int128 和 uint128 类型编写了一个 std::ostream 重载,类似于此答案 (https://stackoverflow.com/a/25115163/753174) 中显示的内容,只是它处理 std::hex、std: :setw、std::setfill 等
当从与 ostream 重载相同的命名空间中调用时,它可以正常工作(例如,如果两者都在全局命名空间中)。如果在其中没有任何其他 ostream 重载的命名空间中调用,它也可以正常工作。例如,这编译得很好并且可以工作:
#include <string>
#include <iostream>
#include <stdint.h>
using uint128 = __uint128_t;
using int128 = __int128_t;
inline std::ostream &operator<<(std::ostream &out, uint128 val)
{
//Obviously not the real implementation, just here as an example
return out << static_cast<uint64_t>(val);
}
void print_uint128(uint128 val)
{
std::cout << "A uint128: " << val << std::endl;
}
namespace Something
{
void print_uint128(uint128 val)
{
std::cout << "A uint128: " << val << std::endl;
}
}
int main()
{
uint128 foo = 1234;
print_uint128(foo);
Something::print_uint128(foo);
return 0;
}
但是,如果在具有 any 其他 std::ostream 重载(甚至是完全不相关的东西,例如任何结构、类、枚举)的命名空间中使用 ostream 运算符,我会收到类似的编译错误这个(尝试过 gcc 8.3 和 10.2,以及 clang 9.0.1)
导致错误的示例 (https://godbolt.org/z/sozrx7):
#include <string>
#include <iostream>
#include <stdint.h>
using uint128 = __uint128_t;
using int128 = __int128_t;
//Doesn't matter what ABC is, an empty struct demonstrates the
//issue, but same happens if ABC is an enum or enum class
struct ABC { };
namespace Something {
std::ostream &operator<<(std::ostream &os, ABC def)
{
//Doesn't matter what this does
return os;
}
}
inline std::ostream &operator<<(std::ostream &out, uint128 val)
{
return out << static_cast<uint64_t>(val);
}
void print_uint128(uint128 val)
{
std::cout << "An int128: " << val << std::endl;
}
namespace Something
{
void print_uint128(uint128 val)
{
std::cout << "An int128: " << val << std::endl;
}
}
int main()
{
uint128 foo = 1234;
print_uint128(foo);
Something::print_uint128(foo);
return 0;
}
错误的完整编译器输出:
<source>:36:32: error: use of overloaded operator '<<' is ambiguous (with operand types 'basic_ostream<char, std::char_traits<char> >' and 'uint128' (aka 'unsigned __int128'))
std::cout << "An int128: " << val << std::endl;
~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:166:7: note: candidate function
operator<<(long __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:170:7: note: candidate function
operator<<(unsigned long __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:174:7: note: candidate function
operator<<(bool __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:178:7: note: candidate function
operator<<(short __n);
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:181:7: note: candidate function
operator<<(unsigned short __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:189:7: note: candidate function
operator<<(int __n);
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:192:7: note: candidate function
operator<<(unsigned int __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:201:7: note: candidate function
operator<<(long long __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:205:7: note: candidate function
operator<<(unsigned long long __n)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:220:7: note: candidate function
operator<<(double __f)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:224:7: note: candidate function
operator<<(float __f)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:232:7: note: candidate function
operator<<(long double __f)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:517:5: note: candidate function [with _Traits = std::char_traits<char>]
operator<<(basic_ostream<char, _Traits>& __out, char __c)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:511:5: note: candidate function [with _CharT = char, _Traits = std::char_traits<char>]
operator<<(basic_ostream<_CharT, _Traits>& __out, char __c)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:523:5: note: candidate function [with _Traits = std::char_traits<char>]
operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
^
/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/ostream:528:5: note: candidate function [with _Traits = std::char_traits<char>]
operator<<(basic_ostream<char, _Traits>& __out, unsigned char __c)
^
1 error generated.
我可以通过在我想使用它的每个命名空间中重新声明我的 int128 ostream 重载,或者通过显式调用 to_string() 函数而不是依赖于 ostream 重载来解决这个问题。但是,这些解决方法并不理想。我错过了什么还是这就是它的样子?据我所知,GCC 和 clang 仍然缺乏这些 128 位类型的 ostream 重载,尽管它们已经存在多年。
【问题讨论】:
-
添加了编译器的完整错误输出,因为我认为它太冗长,所以我剪掉了它。
-
由于
ABC位于不同的命名空间中,因此您不会获得 ADL 便利。您只需要完全命名空间限定所有相关内容,或将 ABC 放在同一个Something命名空间中。 -
我不关心 ABC,它只是一个虚拟结构,表明只要在 Something 命名空间中存在 any 类型的重载,我就会收到此编译器错误尝试打印 int128/uint128 时。
标签: c++