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 intint64_ttypes相同,因此在逻辑上, int64_tlong intlong 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_tlong 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 ,但显然只有两者中的一个。 intlong当然是不同的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位的。 你必须做一些额外的工作来获得你的word32types的定义。 另请参阅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*'