[英]long long int vs. long int vs. int64_t in C++
我在使用 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 int
和int64_t
类型相同,因此从逻辑int64_t
, int64_t
、 long int
和long long int
将是等效的类型 -使用这些类型时生成的程序集是相同的。 一看stdint.h
告诉我原因:
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
在 64 位编译中, int64_t
是long int
,而不是long long int
(显然)。
这种情况的修复非常简单:
#if defined(__GNUC__) && (__WORDSIZE == 64)
template <>
bool is_int64<long long int>() { return true; }
#endif
但这是非常骇人听闻的,并且不能很好地扩展(substance、 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<long int>
将是boost::true_type
,但some_type_trait<long long int>
将不是。 虽然这在 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&, long long int&)
。
我想强调的是,我理解为什么会发生这种情况,但我正在寻找一种不会强迫我到处重复代码的解决方法。
您无需转到 64 位即可看到此类内容。 在常见的 32 位平台上考虑int32_t
。 它可能被typedef
为int
或long
,但显然一次只能是两者之一。 int
和long
当然是不同的类型。
不难看出,在 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>);
您想知道某个类型是否与 int64_t 类型相同,还是想知道某个类型是否为 64 位? 根据您提出的解决方案,我认为您是在问后者。 在那种情况下,我会做类似的事情
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 位类型是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 long
是 64 位的, long long
是 64 位的。 与 32 位一样,Microsoft 在Data Type Ranges 中记录范围,而 long long 仍然是__int64
。
有一个ILP64
数据模型,其中所有内容都是 64 位。 您必须做一些额外的工作才能获得word32
类型的定义。 另请参阅64 位编程模型:为什么是 LP64?
但这是非常骇人听闻的,并且不能很好地扩展(substance、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
并使用 NEON 的 64 位 ARM IoT 小工具:
error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.