long long int vs. long int vs. int64_t in C ++

时间:2010-11-12 01:49:22

标签: c++ gcc cstdint

我在使用C ++类型特征时经历了一些奇怪的行为,并将我的问题缩小到这个古怪的小问题,我将给出大量的解释,因为我不想因为误解而留下任何开放的东西。

假设你有这样的程序:

#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_t类型相同,所以逻辑上,{{ 1}},int64_tlong int将是等效类型 - 使用这些类型时生成的程序集是相同的。一看long long int就会告诉我原因:

stdint.h

在64位编译中,# if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif int64_t,而不是long int(显然)。

解决这种情况非常简单:

long long int

但这是非常强烈的hackish并且不能很好地扩展(物质的实际功能,#if defined(__GNUC__) && (__WORDSIZE == 64) template <> bool is_int64<long long int>() { return true; } #endif 等)。 所以我的问题是:有没有办法告诉编译器uint64_t也是long long int,就像int64_t一样?


我最初的想法是,由于C / C ++类型定义的工作方式,这是不可能的。没有办法为编译器指定基本数据类型的类型等价,因为这是编译器的工作(并且允许这可能会破坏很多东西)并且long int只有一种方式。

我也不太关心在这里得到一个答案,因为这是一个超级优势的边缘案例,我不怀疑任何人都会关心什么时候这些例子不是很可怕(这是否意味着这应该是社区维基?)。


追加:我之所以使用部分模板专精而不是更简单的例子,是因为:

typedef

是所述示例仍将编译,因为void go(int64_t) { } int main() { long long int x = 2; go(x); return 0; } 可隐式转换为long long int


追加:到目前为止,唯一的答案是假设我想知道某个类型是否为64位。我不想误导人们认为我关心这一点,并且可能应该提供更多关于这个问题表现出来的例子。

int64_t

在此示例中,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 ++的类型概念中是有意义的,但这是不可取的。

另一个例子是使用像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 ++(正确地)看到类型不同。 g ++将无法编译,并出现如下错误:无匹配函数调用same_type(long int&, long long int&)

我想强调,我理解为什么这种情况正在发生,但我正在寻找一种不会强迫我在整个地方重复代码的解决方法。

3 个答案:

答案 0 :(得分:45)

你不需要去64位就可以看到这样的东西。在常见的32位平台上考虑int32_t。它可能typedef编辑为intlong,但显然一次只有两个中的一个。 intlong当然是不同的类型。

不难看出在32位系统上没有使int == int32_t == long成为可能的解决方法。出于同样的原因,在64位系统上无法生成long == int64_t == long long

如果可以,对于重载foo(int)foo(long)foo(long long)的代码,可能会产生相当大的后果 - 突然他们对同一个重载有两个定义?!< / p>

正确的解决方案是您的模板代码通常不应该依赖于精确类型,而是依赖于该类型的属性。对于特定情况,整个same_type逻辑仍然可以正常:

long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);

即,当 foo(int64_t)相同时,未定义重载foo(long)

[编辑] 使用C ++ 11,我们现在有一种标准的方式来写这个:

long foo(long x);
std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t);

答案 1 :(得分:5)

你想知道一个类型是否与int64_t的类型相同,或者你想知道某些东西是64位吗?根据您提出的解决方案,我认为您在询问后者。在那种情况下,我会做类似

的事情
template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64

答案 2 :(得分:1)

  

所以我的问题是:有没有办法告诉编译器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位类型是long long

Microsoft将范围记录为Data Type Ranges。说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记录范围为Data Type Ranges且长整数仍为__int64

有一个ILP64数据模型,其中一切都是64位。您必须做一些额外的工作才能获得word32类型的定义。另请参阅64-Bit Programming Models: Why LP64?

等论文
  

但是这是可怕的hackish并且不能很好地扩展(物质的实际功能,uint64_t等)......

是的,它变得更好。 GCC混合并匹配应该采用64位类型的声明,因此即使您遵循特定的数据模型也很容易陷入困境。例如,以下内容会导致编译错误并告诉您使用-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*'