我在使用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 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

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

解决这种情况非常容易:

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

但这真是可怕的刺,并且不缩放不错(实质的实际功能, uint64_t, , ETC)。 所以我的问题是: 有没有办法告诉编译器 long long int 也是一个 int64_t, , 就像 long int 是?


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

我也不太关心在这里得到答案,因为这是一个超级淘汰的案例,我不怀疑有人会在乎何时没有可怕的人为(这意味着这应该是社区Wiki)? 。


附加: :为什么我使用部分模板专业化而不是一个更轻松的示例,例如:

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位即可看到这样的东西。考虑 int32_t 在常见的32位平台上。有可能 typedef'ed as 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);

即,超负荷 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);

其他提示

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

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

因此,我的问题是:有没有办法告诉编译器,长长的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记录了范围 数据类型范围. 。说 long long 等同于 __int64.

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

int:           0
int64_t:       1
long int:      1
long long int: 0

64位Linux使用 LP64 数据模型。渴望是64位, long long 是64位。与32位一样,Microsoft记录了范围 数据类型范围 长时间仍然 __int64.

有个 ILP64 数据模型的所有内容均为64位。您必须做一些额外的工作才能为您的定义 word32 类型。也可以看到像 64位编程模型:为什么要LP64?


但这是可怕的骇人听闻,并且不能很好地扩展(实质,UINT64_T等的实际功能)...

是的,它变得更好。海湾合作委员会的混音和匹配声明应该需要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;

然后,漫步到一个定义的64位手臂IoT小工具 LP64 并使用霓虹灯:

error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top