为什么我不能reinterpret_cast uint 到 int?

Why can't I reinterpret_cast uint to int?

本文关键字:uint int cast 不能 reinterpret 为什么      更新时间:2023-10-16

这就是我想做的:

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

其中随机UintNumber 的类型为 uint64_t

错误是 (MSVC 2010):

错误 C2440:"reinterpret_cast":无法从"常量uint64_t"转换 到"int64_t" 1> 转换是有效的标准转换, 可以隐式执行,也可以使用 static_cast、C 样式执行 演员或功能式演员表

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

因为这不是

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小于 263,则转换结果的值与输入值相同。否则,结果是实现定义的,但我希望所有具有int64_t的已知实现都会定义它来做显而易见的事情:结果相当于输入模 264


如果您希望randomIntNumber具有与randomUintNumber相同的位模式,那么您可以这样做:

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

由于int64_t保证使用 2 的补码表示,因此您希望实现定义 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_t是用于long longunsigned long long的typedefs的实现中,那么我reinterpret_cast就可以了。与实现定义的将超出范围的值转换为有符号类型一样,您可能期望实现的明智做法是使它们成为相应的有符号/无符号类型。因此,就像static_cast和隐式转换一样,您希望它在任何合理的实现中工作,但实际上并不能保证。

在这些情况下使用static_cast。 我想语言设计者以他们所有的智慧决定它被认为不够"不安全",无法保证reinterpret_cast

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

不能使用 显式执行其他转换 reinterpret_cast。

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

来自 C++11 标准(N3376) 5.2.10.1 本文档,第 101 页:

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

如果类型相同,则允许从整型到整型的唯一转换是事实。

其他涉及指针。

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

使用reinterpret_cast您违反了strict aliasing .这很糟糕,因为它最终会导致未定义的行为(也许您使用新的编译器版本编写了故障。什么是严格别名规则,我们为什么关心?是一篇很棒的文章,描述了问题及其解决方案(也许跳过冗长的部分"现在,到规则手册";))。它建议memcpy并争辩说,编译器优化无论如何都会跳过复制。

法典

交互式代码在这里。在您的特定情况下,所有选项都会产生相同的结果。当玩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 << "nn";
    std::cout << "randomUintNumber: " << randomUintNumber << "nn";
    
    // 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 ...nn";
    
    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

为什么不编译?

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

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

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

这不是reinterpret_cast的目的吗?

不,reinterpret_cast用于指针转换。你可以通过在演员阵容内放一个&,在演员表外放一个*来做你想做的事。这就是重新诠释演员表的目的。

reinterpret_cast用于

将对象的存储重新解释为不同的对象。如果您不想通过标准措辞,您可以在此处找到reinterpret_cast可以做的所有事情。通常,您可以记住必须使用指针类型。

因此,如果您真的想将用于uint64_t的位重新解释为 int64_t ,那么这样做:

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

但是,如果您只想转换对象,请尽可能保留其值......只需按照编译器的建议进行操作,然后改用static_cast即可。