为什么我不能reintpret_cast uint到int?

时间:2013-01-31 10:13:51

标签: c++ type-conversion reinterpret-cast

这就是我想要做的事情:

const int64_t randomIntNumber = reinterpret_cast<int64_t> (randomUintNumber);

randomUintNumber的类型为uint64_t

错误是(MSVC 2010):

  

错误C2440:'reinterpret_cast':无法转换为'const uint64_t'   'int64_t'1&gt;转换是有效的标准转换,   这可以隐式执行,也可以使用static -cast,C风格   演员或功能式演员

为什么不编译?两种类型都有相同的位长,不是reinterpret_cast的用途吗?

7 个答案:

答案 0 :(得分:23)

因为那不是reinterpret_cast的用途。所有允许的reinterpret_cast转换都涉及指针或引用,但整数或枚举类型可以是reinterpret_cast。这都是在标准[expr.reinterpret.cast]

中定义的

我不确定您在此处尝试实现的目标,但如果您希望randomIntNumber具有与randomUintNumber相同的值,那么请执行

const int64_t randomIntNumber = randomUintNumber;

如果这会导致编译器警告,或者您只是想更明确,那么:

const int64_t randomIntNumber = static_cast<int64_t>(randomUintNumber);

如果randomUintNumber小于2 63 ,则演员的结果与输入的值相同。否则结果是实现定义的,但我希望所有已知的int64_t实现都会定义它来做明显的事情:结果等同于输入模2 64


如果您希望randomIntNumber具有与randomUintNumber相同的位模式,则可以执行以下操作:

int64_t tmp;
std::memcpy(&tmp, &randomUintNumber, sizeof(tmp));
const int64_t randomIntNumber = tmp;

由于int64_t保证使用二进制补码表示,因此希望实现定义static_cast与超出范围的值具有相同的结果uint64_t。但实际上并没有在标准的AFAIK中得到保证。

即使randomUintNumber是编译时常量,但遗憾的是randomIntNumber 不是编译时常量。但是,“随机”是一个编译时常量? ; - )

如果你需要解决这个问题,并且你不相信这个实现对于将超出范围的无符号值转换为有符号的类型是明智的,那么就像这样:

const int64_t randomIntNumber = 
    randomUintNumber <= INT64_MAX ? 
        (int64_t) randomUintNumber :
        (int64_t) (randomUintNumber - INT64_MAX - 1) + INT64_MIN;

现在,我赞成在可能的情况下编写真正可移植的代码,但即便如此,我认为这也是偏执狂。


不过,你可能想写这个:

const int64_t randomIntNumber = reinterpret_cast<int64_t&>(randomUintNumber);

或等效地:

const int64_t randomIntNumber = *reinterpret_cast<int64_t*>(&randomUintNumber);

这并不能保证能够正常工作,因为虽然它们存在int64_tuint64_t但保证是签名类型和相同大小的无符号类型,但它们实际上并不能保证是标准整数类型的有符号和无符号版本。因此,无论此代码是否违反严格别名,它都是特定于实现的。违反严格别名的代码具有未定义的行为。以下违反严格别名,并且如果randomUintNumber中的位模式是值long long的有效表示,则表示正常:

unsigned long long x = 0;
const long long y = reinterpret_cast<long long &>(x);

因此,int64_tuint64_tlong longunsigned long long的typedef的实现,那么我的reinterpret_cast就可以了。正如实现定义的超出范围值到签名类型的转换一样,您期望实现的明智之处在于使它们成为对应的有符号/无符号类型。就像static_cast和隐式转换一样,你希望它能在任何合理的实现中工作,但实际上并没有保证。

答案 1 :(得分:3)

在这些情况下使用static_cast。我认为语言设计师凭借他们所有的智慧决定保证reinterpret_cast不被认为“不够安全”。

答案 2 :(得分:3)

不,不是。 reinterpret_cast主要用于将现有存储位重新解释为与其不同的类型。很多这些解释都是依赖于实现的,并且标准列出了一个特定的(在这里引用的很长)可以用reinterpret_cast完成的事情列表(主要是在不同的指针/引用类型之间进行转换),但是说:

  

无法使用明确执行其他转换   的reinterpret_cast。

在您的情况下,您可能希望转换类型,而不是重新解释现有存储。为此目的使用static_cast

答案 3 :(得分:1)

从C ++ 11标准版(N3376)5.2.10.1:

  

下面列出了可以使用reinterpret_cast显式执行的转换。没有   可以使用reinterpret_cast明确执行其他转换。

如果类型相同,则允许从积分类型到整数类型的唯一转换。

其他涉及指针。

答案 4 :(得分:1)

reinterpret_cast用于将对象的存储重新解释为不同的对象。如果您不想通过标准措辞,则可以找到reinterpret_cast可以执行here的所有操作。通常你会记得你必须使用指针类型。

因此,如果您真的想将uint64_t用于int64_t的位重新解释为int64_t randomIntNumber = reinterpret_cast<int64_t&> (randomUintNumber); ,请执行以下操作:

static_cast

但是,如果您只想转换对象,请尽可能保留其值...只需执行编译器建议并改为使用{{1}}。

答案 5 :(得分:1)

memcpy重新解释位模式,用static cast变换类型

使用reinterpret_cast违反了strict aliasing。 这很糟糕,因为它最终会导致未定义的行为(也许您使用新的编译器版本对故障进行编码。) What is the Strict Aliasing Rule and Why do we care?是一篇很棒的文章,描述了问题及其解决方案(也许跳过了冗长的部分“ Now,to the Rule-Book”;))。它建议memcpy并认为编译器优化将始终跳过复制。

代码

交互式代码here。在您的特定情况下,所有选项都会产生相同的结果。与newTyperandomUintNumber一起玩时,情况会发生变化。

#include <iostream>
#include <cstring>

int main()
{
    typedef int64_t newType; // try: double, int64_t
    
    uint64_t randomUintNumber = INT64_MAX + 10000; // try: 64000, INT64_MIN, INT64_MAX, UINT64_MAX, INT64_MAX + 10000
    std::cout << "INT64_MIN: " << INT64_MIN << std::endl;
    std::cout << "UINT64_MAX: " << UINT64_MAX << std::endl;
    std::cout << "INT64_MAX: " << INT64_MAX << "\n\n";
    std::cout << "randomUintNumber: " << randomUintNumber << "\n\n";
    
    // const int64_t randomIntNumber = reinterpret_cast<int64_t> (randomUintNumber);
    std::cout << "as \"const int64_t randomIntNumber = reinterpret_cast<int64_t> (randomUintNumber);\" does not work, here are some options ...\n\n";
    
    std::cout << "BAD [undefined behavior!]:" << std::endl;
    const newType a = reinterpret_cast<int64_t&> (randomUintNumber);
    std::cout << "\treinterpret_cast<int64_t&> (randomUintNumber): " << a << std::endl;
    
    const newType b = reinterpret_cast<int64_t&&> (randomUintNumber);
    std::cout << "\treinterpret_cast<int64_t&&> (randomUintNumber): " << b << std::endl;
    
    std::cout << "\nGOOD: " << std::endl;
    const newType c = (int64_t) randomUintNumber;
    std::cout << "\t(int64_t) randomUintNumber [static cast, sometimes reinterprets bit pattern]: " << c << std::endl;
    
    const newType d = static_cast<int64_t>(randomUintNumber);
    std::cout << "\tstatic_cast<int64_t>(randomUintNumber) [the same as before]: " << d << std::endl;
    
    static_assert(sizeof(uint64_t) == sizeof(newType), "should not be taken for granted ...");
    newType eNotConst;
    std::memcpy(&eNotConst, &randomUintNumber, sizeof(uint64_t));
    const newType e = eNotConst;
    std::cout << "\tstd::memcpy(&eNotConst, &randomUintNumber, sizeof(uint64_t)); [definately reinterprets bit pattern]: " << e << std::endl;
    
    
    return 0;
}

输出

INT64_MIN: -9223372036854775808
UINT64_MAX: 18446744073709551615
INT64_MAX: 9223372036854775807

randomUintNumber: 9223372036854785807

as "const int64_t randomIntNumber = reinterpret_cast<int64_t> (randomUintNumber);" does not work, here are some options ...

BAD [undefined behavior!]:
    reinterpret_cast<int64_t&> (randomUintNumber): -9223372036854765809
    reinterpret_cast<int64_t&&> (randomUintNumber): -9223372036854765809

GOOD: 
    (int64_t) randomUintNumber [static cast, sometimes reinterprets bit pattern]: -9223372036854765809
    static_cast<int64_t>(randomUintNumber) [the same as before]: -9223372036854765809
    std::memcpy(&eNotConst, &randomUintNumber, sizeof(uint64_t)); [definately reinterprets bit pattern]: -9223372036854765809

答案 6 :(得分:0)

  

为什么不编译?

因为两种类型都不是指针。

  

两种类型具有相同的位长,

为什么会这么重要?如果它们是指针,也许它们指向相同大小的东西很重要,但它们不是指针。

  

不是reinterpret_cast的用途吗?

不,reinterpret_cast用于指针转换。您可以通过在演员表中放置&并在其外部放置*来执行您想要的操作。这就是重新解释演员的目的。