35

我正在尝试声明一个初始化为某个常量整数值的 constexpr 指针,但是 clang 正在挫败我的所有尝试:

尝试1:

constexpr int* x = reinterpret_cast<int*>(0xFF);

test.cpp:1:20: note: reinterpret_cast is not allowed in a constant expression

尝试2:

constexpr int* x = (int*)0xFF;

test.cpp:1:20: note: cast which performs the conversions of a reinterpret_cast is not allowed in a constant expression

尝试 3:

constexpr int* x = (int*)0 + 0xFF;

test.cpp:1:28: note: cannot perform pointer arithmetic on null pointer

我试图做的事情是设计不允许的吗?如果是这样,为什么?如果没有,我该怎么做?

注意:gcc 接受所有这些。

4

2 回答 2

26

正如 Luc Danton 所指出的,您的尝试被 [expr.const]/2 中的规则阻止了,这些规则说核心常量表达式中不允许使用各种表达式,包括:

-- a reinterpret_cast
-- 具有未定义行为的操作 [注意:包括 [...] 某些指针算术 [...] -- 结束注释]

第一个项目符号排除了您的第一个示例。第二个示例被上面的第一个项目符号排除,加上 [expr.cast]/4 中的规则:

[...] a reinterpret_cast[...] 执行的转换可以使用显式类型转换的强制转换表示法执行。相同的语义限制和行为适用。

第二个项目符号由WG21 核心问题 1313添加,并阐明在常量表达式中不允许对空指针进行指针运算。这排除了您的第三个示例。

即使这些限制不适用于核心常量表达式,仍然无法使用constexpr通过转换整数产生的值来初始化指针,因为 constexpr 指针变量必须由地址常量表达式初始化,通过 [expr .const]/3,必须计算为

具有静态存储持续时间的对象的地址、函数的地址或空指针值。

转换为指针类型的整数不是这些。

g++ 还没有严格执行这些规则,但是它最近的版本已经越来越接近它们,所以我们应该假设它最终会完全实现它们。

如果您的目标是声明一个对其执行静态初始化的变量,您可以简单地删除constexpr--clang 和 g++ 都会为此表达式发出一个静态初始化程序。如果出于某种原因需要将此表达式作为常量表达式的一部分,则有两种选择:

  • 重构代码,以便传递 intptr_t 而不是指针,并在需要时将其转换为指针类型(在常量表达式之外),或者
  • 使用__builtin_constant_p((int*)0xFF) ? (int*)0xFF : (int*)0xFF. 这种精确的表达式形式(__builtin_constant_p在条件运算符的左侧)禁用了条件运算符中严格的常量表达式检查,并且是一个鲜为人知但记录在案的不可移植的 GNU 扩展,两者都支持gcc 和 clang。
于 2012-04-29T22:31:16.530 回答
5

原因是(一次,非常有帮助的)错误消息给出的原因:reinterpret_cast在常量表达式中是不允许的。它被列为 5.19(第 2 段)中的显式例外之一。

将 the 更改reinterpret_cast为 C 风格的转换仍然会以语义等效的 a 结束reinterpret_cast,因此这无济于事(而且消息再次非常明确)。

如果你有办法获得一个带有值的指针,0你确实可以使用p + 0xff,但我想不出一种方法来获得这样一个带有常量表达式的指针。您可以依赖空指针值(0在您所做的指针上下文中,或者nullptr)在您的实现中具有值,0但正如您所看到的那样,您的实现拒绝这样做。我认为这是允许的。(例如,允许实现对大多数常量表达式进行保释。)

于 2012-04-29T08:47:33.177 回答