关于gcc:C ++中long long int vs.long int vs.int64_t

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

在使用C ++类型特征时,我经历了一些奇怪的行为,并且将我的问题缩小到这个古怪的小问题,由于我不想留下任何可能引起误解的信息,因此我将作大量解释。

假设您有一个像这样的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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位编译中,程序的输出为:

1
2
3
4
int:           0
int64_t:       1
long int:      0
long long int: 1

但是,由64位GCC编译产生的程序将输出:

1
2
3
4
int:           0
int64_t:       1
long int:      1
long long int: 0

这很奇怪,因为long long int是一个带符号的64位整数,并且就所有意图和目的而言,都与long intint64_t类型相同,因此从逻辑上讲,int64_tlong intlong long int将是等效类型-使用这些类型时生成的程序集是相同的。一看stdint.h告诉我原因:

1
2
3
4
5
6
# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

在64位编译中,int64_tlong int,而不是long long int(显然)。

解决这种情况非常简单:

1
2
3
4
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif

但这是骇人听闻的技巧,而且扩展性不好(实体的实际功能,uint64_t等)。所以我的问题是:有没有办法告诉编译器long long int还是int64_t,就像long int一样?

我最初的想法是,由于C / C ++类型定义的工作方式,这是不可能的。没有一种方法可以为编译器指定基本数据类型的类型等效性,因为这是编译器的工作(允许这样做可能会破坏很多事情),而typedef仅是一种方法。

我也不是很想在这里得到答案,因为这是一个超级骗局,我不怀疑任何人都不会关心这些示例的设计错误(这是否应该是社区Wiki?)。 。

附录:之所以使用部分模板专业化,而不是像下面这样的简单示例,是因为

1
2
3
4
5
6
7
8
void go(int64_t) { }

int main()
{
    long long int x = 2;
    go(x);
    return 0;
}

就是说,由于long long int可隐式转换为int64_t,因此上述示例仍将编译。

追加:到目前为止,唯一的答案是假设我想知道类型是否为64位。我不想误导人们以为我对此很在意,并且可能应该提供更多示例说明此问题在何处显现。

1
2
3
4
5
template <typename T>
struct some_type_trait : boost::false_type { };

template <>
struct some_type_trait<int64_t> : boost::true_type { };

在此示例中,some_type_trait将是boost::true_type,但some_type_trait不会。尽管这在C ++的类型概念中是有意义的,但并不理想。

另一个示例使用的是类似same_type的限定符(在C ++ 0x Concepts中非常常见):

1
2
3
4
5
6
7
8
9
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&)

我想强调指出,我理解为什么会这样,但是我正在寻找一种解决方法,它不会迫使我在各处重复执行代码。


您无需转到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)重载的代码,可能的后果将是非常痛苦的-突然之间,对于同一个重载,它们将具有两个定义?

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

1
2
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,我们现在有了一种标准的编写方式:

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


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

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


So my question is: Is there a way to tell the compiler that a long long int is the also a int64_t, just like long int is?

这是一个很好的问题,但我怀疑答案是否定的。

另外,long int可能不是long long int

1
2
3
4
5
6
# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif

我相信这是libc。我怀疑你想更深入。

In both 32-bit compile with GCC (and with 32- and 64-bit MSVC), the
output of the program will be:

1
2
3
4
int:           0
int64_t:       1
long int:      0
long long int: 1

32位Linux使用ILP32数据模型。整数,long和指针均为32位。 64位类型是long long

Microsoft在"数据类型范围"中记录了范围。口语long long等同于__int64

However, the program resulting from a 64-bit GCC compile will output:

1
2
3
4
int:           0
int64_t:       1
long int:      1
long long int: 0

64位Linux使用LP64数据模型。 long是64位,而long long是64位。与32位一样,Microsoft以数据类型范围记录范围,并且long long仍为__int64

有一个ILP64数据模型,其中的所有数据都是64位。您必须做一些额外的工作才能获得word32类型的定义。另请参见诸如64位编程模型之类的论文:为什么选择LP64?

But this is horribly hackish and does not scale well (actual functions of substance, uint64_t, etc)...

是的,它变得更好。 GCC混合并匹配应该采用64位类型的声明,因此即使您遵循特定的数据模型,也很容易遇到麻烦。例如,以下内容导致编译错误,并告诉您使用-fpermissive

1
2
3
4
5
6
7
8
9
10
11
12
#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);

结果是:

1
error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'

因此,忽略LP64并将其更改为:

1
typedef unsigned long long word64;

然后,转到定义LP64的64位ARM IoT小工具并使用NEON:

1
error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'