【问题标题】:Why can't I reinterpret_cast uint to int?为什么我不能将 uint 重新解释为 int?
【发布时间】:2013-01-15 09:44:24
【问题描述】:

这是我想做的:

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

其中 randomUintNumber 的类型为 uint64_t

错误是(MSVC 2010):

错误 C2440:“reinterpret_cast”:无法从“const uint64_t”转换 to 'int64_t' 1> 转换是有效的标准转换, 可以隐式执行,也可以使用 static_cast,C 风格 演员表或函数式演员表

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

【问题讨论】:

  • 如果你能列出你得到的错误,我们可能更容易说为什么它不能编译。
  • @JoachimPileborg:我的错,扩大了问题。

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


【解决方案1】:

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

我不确定你想在这里实现什么,但如果你希望 randomIntNumberrandomUintNumber 具有相同的值,那么就这样做

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 保证使用二进制补码表示,您会希望实现定义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 的类型定义的实现中,我的reinterpret_cast 就可以了。与实现定义的超出范围值到有符号类型的转换一样,您期望实现的明智之举是使它们对应有符号/无符号类型。因此,就像static_cast 和隐式转换一样,您希望它可以在任何合理的实现中工作,但实际上并不能保证。

【讨论】:

    【解决方案2】:

    在这些情况下使用static_cast。我想语言设计者在他们所有的智慧中都认为它不是“足够不安全”来保证reinterpret_cast

    【讨论】:

      【解决方案3】:

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

      无法使用显式执行其他转换 reinterpret_cast。

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

      【讨论】:

      • 但重新诠释正是我想要的!我不确定 static_cast(或 C-cast)是否会产生所需的结果,必须先刷新我对它的工作原理的记忆。
      • @VioletGiraffe:如果您想将某个存储重新解释为不同的类型,那么您应该重新解释一个指向该存储的指针。请注意,您在此处输入 IB/UB 土地,尤其是在取消引用结果时。
      • 我怎么会忘记...这正是我上次遇到这个问题时解决这个问题的方法(将指针转换为目标类型并取消引用它)。 IB/UB究竟出现在哪里?
      • @VioletGiraffe:潜在的 UB 是由于严格的别名造成的,请参阅我的回答。
      【解决方案4】:

      来自 C++11 标准(N3376)5.2.10.1 this document, page 101

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

      从整数类型到整数类型的唯一转换实际上是在类型相同的情况下。

      其他涉及指针。

      【讨论】:

        【解决方案5】:

        为什么不编译?

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

        两种类型的位长相同,

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

        这不是 reinterpret_cast 的用途吗?

        不,reinterpret_cast 用于指针转换。你可以做你想做的事,把&amp;放在演员表里面,把*放在演员表外面。这就是重新解释演员表的目的。

        【讨论】:

        • 位长很重要,因为很明显,您在按位重新解释不同长度的类型时会遇到问题。
        • 这是对reinterpret_cast的常见误解。从概念上讲,它不会进行按位重新解释(尽管在通用平台上它“碰巧”)。例如,C++ 标准 (5.2.10) 说使用 reinterpret_cast,“指针可以显式转换为任何大到足以容纳它的整数类型”。 -- 包括更大的 整数类型。而且,它明确表示它“可能会或可能不会产生与原始值不同的表示”。
        • @VioletGiraffe 在使用reinterpret_cast 的结果时,指向(或引用)对象的位长很重要。指针本身的位长无关紧要:您可以在 char*int* 之间使用 reinterpret_cast,即使 char*int* 具有不同的大小(就像它们有时在旧机器上所做的那样)。
        【解决方案6】:

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

        因此,如果您真的想将用于 uint64_t 的位重新解释为 int64_t,请执行以下操作:

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

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

        【讨论】:

          【解决方案7】:

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

          使用reinterpret_cast 违反了strict aliasing。 这很糟糕,因为它最终会导致未定义的行为(也许您使用新的编译器版本编码故障。) What is the Strict Aliasing Rule and Why do we care? 是一篇很棒的文章,描述了问题及其解决方案(也许可以跳过冗长的部分“现在,到规则手册”;))。它推荐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
          

          【讨论】:

            猜你喜欢
            • 2012-04-20
            • 2016-07-19
            • 1970-01-01
            • 1970-01-01
            • 2020-05-30
            • 1970-01-01
            • 1970-01-01
            • 2016-02-02
            • 1970-01-01
            相关资源
            最近更新 更多