【问题标题】:Why is reinterpret_cast not constexpr?为什么 reinterpret_cast 不是 constexpr?
【发布时间】:2020-05-11 18:35:10
【问题描述】:

考虑以下 sn-p:

static constexpr uint8_t a = 0;
static constexpr const int8_t *b = reinterpret_cast<const int8_t *>(&a);

使用error: a reinterpret_cast is not a constant expression 编译失败,因为the C++ standard forbidsconstexpr 中使用了reinterpret_cast

但是,如果我想将值 b 存储在 PROGMEM 中(对于 AVR 微控制器),编译会成功:

static constexpr uint8_t a = 0;
static const int8_t PROGMEM *const b = reinterpret_cast<const int8_t *>(&a);

在这种情况下,编译器能够证明表达式reinterpret_cast&lt;const int8_t *&gt;(&amp;a) 是编译时常量,因为它将其结果(指向包含零的某个字节的地址)插入到二进制程序空间中:

_ZL1g:
  .zero   1
  .section        .progmem.data,"a",@progbits
  .type   _ZL1b, @object
  .size   _ZL1b, 2
_ZL1b:
  .word   _ZL1g

另外,我的理解是reinterpret_cast 是一个编译时指令。那怎么不能在constexpr里面使用呢?

【问题讨论】:

  • 不是很好,但解决方法是从 void 传递...所以static_cast&lt;uint8_t*&gt;(static_cast&lt;void*&gt;(ptr))

标签: c++ c++11 avr constexpr reinterpret-cast


【解决方案1】:

在运行时,C++ 语言具有未定义行为的概念。在某些(明确指定的)条件下,程序具有未定义的行为,这意味着它可以表现出任何行为:它可以崩溃,它可以永远挂起,它可以打印乱码,它可以看起来工作,或者它可以做任何事情。为什么存在这种情况的一个简单解释是性能。

在运行时这是一个折衷(如果你愿意的话可以妥协),但在编译时这是不可接受的。如果标准允许在编译时进行 UB,那么不仅在编译程序或无限编译时发生崩溃是合法的,而且您永远无法确定编译后的可执行文件的有效性。

因此,任何形式的constexpr 都必须 100% 没有未定义行为。没有例外。没有余地。

一个臭名昭著的 UB 来源是reinterpret_castreinterpret_cast 的有效用法很少,大多数会导致 UB。此外,实际上不可能检查使用是否有效。所以reinterpret_cast在编译过程中是不允许的,即在constexpr中是不允许的。

【讨论】:

  • @Acorn 它会使语言过于复杂而无法允许某些用途,而这些用途无论如何都只是所有有效案例的一个子集。委员会决定反对它,转而使用 constexpr 容器来解决几乎所有需要 reinterpret_cast 的问题
  • @Acorn: "像 OP 描述的情况很容易被允许。" 如果你不打算在编译器中实现它们,那么我不会这么快就说任何这样的事情都是“容易的”。在不使用reinterpret_cast 的情况下进行等效操作很简单,而且可读性更强。您可以根据需要在有符号和无符号之间静态转换值。这样,当您将值从一种类型转移到另一种类型时,每个人都 100% 清楚,并且实现不必经常处理编译器应该使用哪种解释的问题。
  • @NicolBolas “在不使用reinterpret_cast 的情况下做同样的事情很简单” 实际上不复制数据是不可能的,因为error: static_cast from 'const uint8_t *' to 'const int8_t *' is not allowed。而static_cast 产生一个右值,如果不先存储就无法转换为指针值。
  • @ConorTaylor: "不复制数据" 谁认为这是对你正在做的任何事情的必要限制?这是一个字节;我认为您可以负担得起一个字节的副本。我的观点是,您可以更直接地实现相同的目标:当您想以不同的方式访问该值时,只需复制它即可。
  • @NicolBolas 我发布的 sn-ps 是对问题的简化,实际对象比一个字节大得多。它适用于嵌入式环境。因此,对于我的情况,这是一个必要的限制,但对于一般的 constexpr reinterpret_cast 问题来说不是,您当然可以在其中 static_cast 并存储在左值中。
【解决方案2】:

那么为什么不能在 constexpr 中使用呢?

仅仅是因为标准不允许。 constexpr 是自 C++11 以来在不同标准上不断扩展的功能,因此很自然地认为 reinterpret_cast 使用的子集可以工作。

问题是允许它实际上是有用的还是有害的。 reinterpret_cast 很少有好的用途,特别是如果您在假设严格的别名规则成立的情况下编写和编译代码:很容易创建破坏它的指针。

另一方面,很明显,对于嵌入式用户和专门的编译器/标志/环境,它可能在某种程度上有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-27
    • 2020-10-30
    • 1970-01-01
    • 2021-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-25
    相关资源
    最近更新 更多