【问题标题】:long long int vs. long int vs. int64_t in C++C++ 中的 long long int 与 long int 与 int64_t
【发布时间】:2011-05-08 20:07:07
【问题描述】:

我在使用 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 位编译(以及使用 32 位和 64 位 MSVC)中,程序的输出将是:

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 类型相同,因此从逻辑上讲,int64_tlong intlong long int 将是等效类型 - 使用这些类型时生成的程序集是相同的。一看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

但这是非常骇人听闻的,并且不能很好地扩展(物质的实际功能,uint64_t 等)。 所以我的问题是:有没有办法告诉编译器long long int 也是int64_t,就像long int 一样?


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

我也不太关心在这里得到答案,因为这是一个超级极端的边缘案例,我不怀疑任何人会关心如果示例不是非常人为设计的(这是否意味着这应该是社区维基?)。


追加:我使用部分模板特化而不是更简单的示例的原因,例如:

void go(int64_t) { }

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

因为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 { };

在此示例中,some_type_trait&lt;long int&gt; 将是 boost::true_type,但 some_type_trait&lt;long long int&gt; 不会。虽然这在 C++ 的类型概念中是有道理的,但这是不可取的。

另一个例子是使用像 same_type 这样的限定符(这在 C++0x 概念中很常见):

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&amp;, long long int&amp;)

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

【问题讨论】:

  • 出于好奇,您的示例程序对sizeof 每种类型的结果是否相同?也许编译器对long long int 的大小有不同的处理。
  • 你编译时启用了 C++0x 吗? C ++ 03 没有&lt;cstdint&gt;,所以它不得不说“这是一个扩展”(它就是这样)的事实可能是在吓唬它。
  • 是的,我可能应该指定我正在使用 --std=c++0x 。是的,sizeof(long long int) == sizeof(long int) == sizeof(int64_t) == 8
  • 还没有人提到这一点,但万一被忽视了:longlong long 是不同的类型(即使它们具有相同的大小和表示形式)。 int64_t 始终是另一种现有类型的别名(尽管有它的名称,typedef 不会创建新类型,它只是为已经存在的类型提供别名)
  • 答案/cmets 中缺少一个重要的陈述,当我遇到这个怪癖时,这对我很有帮助:永远不要使用固定大小的类型来可靠地专门化模板。始终使用基本类型并涵盖所有可能的情况(即使您使用固定大小的类型来实例化这些模板)。 所有可能的情况意味着:如果您需要使用int16_t 进行实例化,则使用shortint 进行专门化,您将被覆盖。 (如果您喜欢冒险,请使用signed char

标签: c++ gcc cstdint


【解决方案1】:

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

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

【讨论】:

  • 你不是缺少return 和分号吗?
  • 不过,您应该为此使用sizeof
  • long long int 和 long int 无论大小是否相同,都不是同一类型。行为没有错误。那只是 C++。
  • 这不是名义打字的限制。这是 meaningless 名义类型的限制。在过去,事实上的标准是short = 16 位,long = 32 位,int = 原生大小。在这些 64 位时代,intlong 不再意味着任何东西。
  • @dan04:它们没有比以往更多或更少的意义。 short 至少 16 位,int 至少为 16 位,long 至少为 32 位,带有(以下是草率的符号)short
【解决方案2】:

您无需转到 64 位即可查看此类内容。考虑常见 32 位平台上的 int32_t。它可能是typedef'ed 为intlong,但显然一次只有两者之一。 intlong 当然是不同的类型。

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

如果可以的话,对于重载 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);

即,重载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);

[编辑] 或 C++20

long foo(long x);
int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>);

【讨论】:

  • 悲伤的消息是,例如在 64 位 MSVC19 (2017) 上 sizeof() longint 相同,但 std::is_same&lt;long, int&gt;::value 返回 false。 OSX HighSierra 上的 AppleClang 9.1 也有同样的怪癖。
  • @Ax3l:这并不奇怪。自 ISO C 90 以来,几乎每个编译器都至少有一对这样的编译器。
  • 没错,它们是不同的类型。
【解决方案3】:

所以我的问题是:有没有办法告诉编译器 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 位编译中(以及使用 32 位和 64 位 MSVC), 程序的输出将是:

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 数据模型。 Long 是 64 位的,long long 是 64 位的。与 32 位一样,Microsoft 将范围记录在 Data Type Ranges 并且 long long 仍然是 __int64

有一个 ILP64 数据模型,其中所有内容都是 64 位的。您必须做一些额外的工作才能获得word32 类型的定义。另见64-Bit Programming Models: Why LP64?等论文


但这是非常骇人听闻的,并且不能很好地扩展(物质的实际功能,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*'

【讨论】:

    猜你喜欢
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多