C++:long long int vs. long int vs. int64_t

Tra*_*kel 81 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;
}
Run Code Online (Sandbox Code Playgroud)

在使用GCC(以及32位和64位MSVC)进行32位编译时,程序的输出将为:

int:           0
int64_t:       1
long int:      0
long long int: 1
Run Code Online (Sandbox Code Playgroud)

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

int:           0
int64_t:       1
long int:      1
long long int: 0
Run Code Online (Sandbox Code Playgroud)

这是好奇的,因为long long int是有符号的64位整数,并且为所有意图和目的,等同于long intint64_t类型,所以在逻辑上,int64_t,long intlong long int将等效类型-使用这些类型时所产生的组件是相同的.一看就stdint.h告诉我原因:

# if __WORDSIZE == 64
typedef long int  int64_t;
# else
__extension__
typedef long long int  int64_t;
# endif
Run Code Online (Sandbox Code Playgroud)

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

解决这种情况非常简单:

#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
Run Code Online (Sandbox Code Playgroud)

但这是可怕的hackish并且不能很好地扩展(物质的实际功能uint64_t等). 所以我的问题是:有没有办法告诉编译器a long long int也是a int64_t,就像long int是?


我最初的想法是,由于C/C++类型定义的工作方式,这是不可能的.没有办法为编译器指定基本数据类型的类型等价,因为这是编译器的工作(并允许它可以打破很多东西)并且typedef只采用单向方式.

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


附加:我之所以使用部分模板特化而不是更简单的示例,例如:

void go(int64_t) { }

int main()
{
    long long int x = 2;
    go(x);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

是说所述示例仍然会编译,因为long long int可以隐式转换为int64_t.


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

template <typename T>
struct some_type_trait : boost::false_type { };

template <>
struct some_type_trait<int64_t> : boost::true_type { };
Run Code Online (Sandbox Code Playgroud)

在这个例子中,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);
}
Run Code Online (Sandbox Code Playgroud)

该示例无法编译,因为C++(正确地)看到类型不同.g ++将无法编译,错误如下:没有匹配的函数调用same_type(long int&, long long int&).

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

MSa*_*ers 47

你不需要去64位看这样的东西.考虑int32_t常见的32位平台.它可能是typedef' int或作为一个long,但显然只是两者中的一个.intlong当然是不同的类型.

不难看出int == int32_t == long在32位系统上没有解决方法.出于同样的原因,没有办法long == int64_t == long long在64位系统上进行.

如果可以的话,可能的后果对于重载的代码来说会相当痛苦foo(int),foo(long)并且foo(long long)- 突然他们对同一个重载有两个定义?!

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

long foo(long x);
std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t);
Run Code Online (Sandbox Code Playgroud)

即,过载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);
Run Code Online (Sandbox Code Playgroud)

  • @ Ax3l:这并不奇怪.实际上,自ISO C 90以来,每个编译器都至少有一对这样的编译器. (2认同)

Log*_*ldo 5

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

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
Run Code Online (Sandbox Code Playgroud)

  • 不再.无论如何,正确的语法被高估了;) (11认同)
  • long long int和long int的类型不同,无论它们是否大小相同.这种行为并非错误.那只是C++. (5认同)
  • 这不是名义打字的限制.这是*无意义*名义打字的限制.在过去,事实上的标准是"short"= 16位,"long"= 32位,"int"=原生大小.在64位的这些日子里,`int`和`long`不再*表示任何东西了. (5认同)