C ++:long long int长整型与int64_t
我在使用C ++types特征的时候遇到了一些奇怪的行为,并且把我的问题缩小到了这个古怪的小问题上,我会给出很多的解释,因为我不想留下任何可能导致误解的东西。
假设你有这样一个程序:
#include <iostream> #include <cstdint> template <typename T> bool is_int64() { return false; } template <> bool is_int64<int64_t>() { return true; } int main() { std::cout << "int:\t" << is_int64<int>() << std::endl; std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl; std::cout << "long int:\t" << is_int64<long int>() << std::endl; std::cout << "long long int:\t" << is_int64<long long int>() << std::endl; return 0; }
在使用GCC(32位和64位MSVC)的32位编译中,程序的输出是:
int: 0 int64_t: 1 long int: 0 long long int: 1
但是,由64位GCC编译产生的程序将输出:
int: 0 int64_t: 1 long int: 1 long long int: 0
这很奇怪,因为long long int
是一个有符号的64位整数,对于所有的意图和目的而言,与long int
和int64_t
types相同,因此在逻辑上, int64_t
, long int
和long long int
将是等效types -使用这些types时生成的程序集是相同的。 看stdint.h
告诉我为什么:
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif
在64位编译中, int64_t
是long int
,而不是long long int
(很明显)。
解决这种情况非常简单:
#if defined(__GNUC__) && (__WORDSIZE == 64) template <> bool is_int64<long long int>() { return true; } #endif
但是这是非常可怕的,并不能很好地扩展(物质的实际function, uint64_t
等)。 所以我的问题是:有没有办法告诉编译器, long long int
也是一个int64_t
,就像long int
是?
我最初的想法是,这是不可能的,由于C / C ++types定义的工作方式。 没有办法指定编译器的基本数据types的types等价,因为这是编译器的工作(并且允许这会破坏很多东西), typedef
只能用一种方法。
我也不太在意在这里得到答案,因为这是一个超级困难的边缘案例,我不怀疑任何人会关心什么时候这些例子没有可怕的devise(这是否意味着这应该是社区维基?) 。
追加 :为什么我使用部分模板专业化,而不是一个简单的例子,如:
void go(int64_t) { } int main() { long long int x = 2; go(x); return 0; }
是说所述的例子仍然会编译,因为long long int
可以隐式转换为int64_t
。
追加 :到目前为止唯一的答案假设我想知道一个types是64位。 我不想误导人们认为我在乎这一点,可能应该提供更多这个问题performance出来的例子。
template <typename T> struct some_type_trait : boost::false_type { }; template <> struct some_type_trait<int64_t> : boost::true_type { };
在这个例子中, some_type_trait<long int>
将是boost::true_type
,但是some_type_trait<long long int>
将不会是。 虽然这在C ++的types的思想中是有意义的,但这并不可取。
另一个例子是使用像same_type
这样的限定符(这在C ++ 0x Concepts中非常常见):
template <typename T> void same_type(T, T) { } void foo() { long int x; long long int y; same_type(x, y); }
这个例子无法编译,因为C ++(正确)看到types是不同的。 g ++将无法编译错误,如:没有匹配的函数调用same_type(long int&, long long int&)
。
我想强调,我明白为什么会发生这种情况,但是我正在寻找一种解决方法,不会强制我在所有地方重复代码。
你不需要去64位看到这样的事情。 在普通的32位平台上考虑int32_t
。 它可能被定义为int
或者long
int
,但显然只有两者中的一个。 int
和long
当然是不同的types。
不难看出,在32位系统上没有使int == int32_t == long
解决方法。 出于同样的原因,没有办法在64位系统上long == int64_t == long long
。
如果可以的话,对于重载foo(int)
, foo(long)
和foo(long long)
代码来说,可能产生的后果将是相当痛苦的 – 突然间他们对于同一个过载有两个定义?
正确的解决scheme是你的模板代码通常不应该依赖于一个精确的types,而是依赖于那个types的属性。 对于特定的情况,整个same_type
逻辑仍然可以:
long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
也就是说,当它和foo(long)
完全一样时, foo(int64_t)
的重载没有被定义。
用C ++ 11,我们现在有一个标准的写法:
long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);
你想知道一个types是否与int64_ttypes相同,或者你想知道是否有64位? 基于你提出的解决scheme,我想你是在问后者。 在这种情况下,我会做类似的事情
template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
所以我的问题是:有没有办法告诉编译器,long long int也是一个int64_t,就像long int是?
这是一个很好的问题或问题,但我怀疑答案是否定的。
另外,一个long int
可能不是一个long long int
。
# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif
我相信这是libc。 我怀疑你想要更深入。
在使用GCC(32位和64位MSVC)的32位编译中,程序的输出是:
int: 0 int64_t: 1 long int: 0 long long int: 1
32位Linux使用ILP32数据模型。 整数,长整数和指针是32位的。 64位types是long long
。
Microsoft在“ 数据types范围”中logging范围 。 long long
的说法相当于__int64
。
但是,由64位GCC编译产生的程序将输出:
int: 0 int64_t: 1 long int: 1 long long int: 0
64位Linux使用LP64
数据模型。 长整型是64位, long long
整型是64位。 与32位一样,Microsoft在“ 数据types范围”中logging范围 ,long long仍然是__int64
。
有一个ILP64
数据模型,其中一切都是64位的。 你必须做一些额外的工作来获得你的word32
types的定义。 另请参阅64位编程模型:为什么selectLP64?
但是这是非常可怕的,不能很好地扩展(物质的实际function,uint64_t等)…
对,它变得更好。 GCC混合并匹配应该采用64位types的声明,所以即使您遵循特定的数据模型,也很容易陷入麻烦。 例如,以下会导致编译错误,并告诉您使用-fpermissive
:
#if __LP64__ typedef unsigned long word64; #else typedef unsigned long long word64; #endif // intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864) // extern int _rdrand64_step(unsigned __int64 *random_val); // Try it: word64 val; int res = rdrand64_step(&val);
它导致:
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'
所以,忽略LP64
并将其更改为:
typedef unsigned long long word64;
然后,漫步到定义LP64
的64位ARM IoT小工具,并使用NEON:
error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'