c++ - C++ 中的 long long int 与 long int 与 int64_t

标签 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 位编译(以及使用 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 int 相同。和 int64_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

在 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只有一条路。

我也不太关心在这里得到答案,因为这是一个 super 骗局的边缘案例,我不怀疑当示例不是非常人为时有人会关心(这是否意味着这应该是社区维基?)。


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

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 编成 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>);

关于c++ - C++ 中的 long long int 与 long int 与 int64_t,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4160945/

相关文章:

c++ - 获取要使用的 move 构造函数/赋值运算符

c++ - 全局变量不好吗?

大型数组中的 C++ 性能

c++ - Qt/GCC : Returning an object/nullptr from a function by value. 为什么这段代码可以编译?

c++ - 标准是否保证 uint8_t、int8_t 和 char 都是唯一类型?

c++ - 从url下载图片并放入缓冲区

c++ - 如何仅在本地 header 上运行预处理器?

c - 预期的声明说明符或 '...' token 之前的 '('?

c++11 - C++ 11 uint类型与u_int

c++ - (C++11) 如果使用 cstdint 类型,g++ 不提供适当的警告 'incorrectly'