[英]Why is there ambiguity between uint32_t and uint64_t when using size_t on Mac OS X?
考虑以下示例代码:
#include <iostream>
#include <inttypes.h>
using namespace std;
int f(uint32_t i)
{
return 1;
}
int f(uint64_t i)
{
return 2;
}
int main ()
{
cout << sizeof(long unsigned) << '\n';
cout << sizeof(size_t) << '\n';
cout << sizeof(uint32_t) << '\n';
cout << sizeof(uint64_t) << '\n';
//long unsigned x = 3;
size_t x = 3;
cout << f(x) << '\n';
return 0;
}
这在 Mac OSX 上失败了:
$ g++ --version
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
$ make test
g++ test.cc -o test
test.cc: In function 'int main()':
test.cc:23: error: call of overloaded 'f(size_t&)' is ambiguous
test.cc:6: note: candidates are: int f(uint32_t)
test.cc:10: note: int f(uint64_t)
make: *** [test] Error 1
为什么? 因为 'size_t' 应该是无符号的并且是 32 位或 64 位宽。 那么歧义在哪里呢?
尝试使用“unsigned long x”而不是“size_t x”进行相同操作会导致出现类似的歧义错误消息。
在 Linux/Solaris 系统上,使用不同的 GCC 版本、不同的体系结构等进行测试。没有歧义报告(并且在每个体系结构上使用了正确的重载)。
这是 Mac OS X 的错误还是功能?
在 Mac OS 下,这些类型定义为:
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
其中size_t
被定义为__SIZE_TYPE__
:
#if defined(__GNUC__) && defined(__SIZE_TYPE__)
typedef __SIZE_TYPE__ __darwin_size_t; /* sizeof() */
#else
typedef unsigned long __darwin_size_t; /* sizeof() */
#endif
因此,如果您将代码更改为:
#include <iostream>
#include <inttypes.h>
using namespace std;
int f(uint32_t i)
{
return 1;
}
int f(uint64_t i)
{
return 2;
}
int f (unsigned long i)
{
return 3;
}
int main ()
{
cout << sizeof(unsigned long) << '\n';
cout << sizeof(size_t) << '\n';
cout << sizeof(uint32_t) << '\n';
cout << sizeof(uint64_t) << '\n';
//long unsigned x = 3;
size_t x = 3;
cout << f(x) << '\n';
return 0;
}
并运行它,你会得到:
$ g++ -o test test.cpp
$ ./test
8
8
4
8
3
如果你真的想要,你可以像这样实现你想要的语义:
#define IS_UINT(bits, t) (sizeof(t)==(bits/8) && \
std::is_integral<t>::value && \
!std::is_signed<t>::value)
template<class T> auto f(T) -> typename std::enable_if<IS_UINT(32,T), int>::type
{
return 1;
}
template<class T> auto f(T) -> typename std::enable_if<IS_UINT(64,T), int>::type
{
return 2;
}
并不是说这是个好主意; 只是说你可以做到。
可能有一个很好的标准 C++ 方法来询问编译器“这两种类型是否相同,你知道我的意思,别跟我装傻”,但如果有,我不知道。
2020 年更新:如果没有宏,您本可以更习惯地完成它。 C++14 给了我们简写enable_if_t
而 C++17 给了我们is_integral_v
:
template<int Bits, class T>
constexpr bool is_uint_v =
sizeof(T)==(Bits/8) && std::is_integral_v<T> && !std::is_signed_v<T>;
template<class T> auto f(T) -> std::enable_if_t<is_uint_v<32, T>, int>
{ return 1; }
template<class T> auto f(T) -> std::enable_if_t<is_uint_v<64, T>, int>
{ return 2; }
然后在 C++20 中我们有更短的速记requires
:
template<int Bits, class T>
constexpr bool is_uint_v =
sizeof(T)==(Bits/8) && std::is_integral_v<T> && !std::is_signed_v<T>;
template<class T> int f(T) requires is_uint_v<32, T> { return 1; }
template<class T> int f(T) requires is_uint_v<64, T> { return 2; }
甚至更短更短的速记“缩写函数模板”(尽管坦率地说这是混淆的,我不会在现实生活中推荐它):
template<class T, int Bits>
concept uint =
sizeof(T)==(Bits/8) && std::is_integral_v<T> && !std::is_signed_v<T>;
int f(uint<32> auto) { return 1; } // still a template
int f(uint<64> auto) { return 2; } // still a template
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.