是的,可以用常量表达式计算基类的位移,但它根本不是可移植的。您可以使用一个鲜为人知但 documented gcc 扩展,它也受 clang 支持。涉及到__builtin_constant_p与运算符?:一起使用时的使用:
#define CB (&(B&)*(C*)nullptr)
constexpr auto c_b_address = __builtin_constant_p CB ? CB : CB;
请注意,我刚刚使用宏 CB 来说明发生了什么。当然,这也可以通过多次重复表达式来完成。顺便说一句,我第一次知道这个技巧in this question,其中包含有用的背景信息。
您可能已经了解,基本问题是constexpr 表达式中既不允许使用reinterpret_cast,也不允许使用等效的C 风格转换。奇怪的是,C 风格的演员表(如上)被接受,但 reinterpret_cast(不会生成任何代码)不被接受。我还尝试了晦涩但看似合适的->* 运算符,但结果好坏参半:
#define CB1 (&(B&)*(C*)nullptr)
#define CB2 (&((reinterpret_cast<C*>(nullptr))->*(&C::b)))
#define CB3 (&(((C*)(nullptr))->*(&C::b)))
#define CB4 (&(B&)*reinterpret_cast<C*>(nullptr))
#define CB CB1
g++ 4.8.3 和 clang++ 3.4 的结果:
g++ clang++
--- ------------ --------
CB1 OK OK
CB2 compile error compile error
CB3 OK compiles but gives answer = 0
CB4 compile error compile error
在我运行 Linux 的 64 位机器上,只有CB1 使用两种编译器产生正确答案 4。使用 gcc,CB1 和 CB2 都可以使用或不使用 __builtin_constant_p。使用 clang 唯一有效的版本是 CB1 和 __builtin_constant_p。
我们为什么要相信这是故意行为?
正如@ShafikYaghmour 在评论中相当合理地询问的那样,“您是否有 gcc 或 clang 参考表明他们支持这种行为?”我将把它扩大到问“存在哪些文件表明这是故意的,而不仅仅是一个奇怪的副作用?”毕竟,如果有人真的要使用它,最好有一些迹象表明它将来可能会继续存在。本节试图解决这个问题。
对于 clang,引用是 the source code itself,其中 VisitConditionalOperator 函数中的注释说:
// If the condition (ignoring parens) is a __builtin_constant_p call,
// the result is a constant expression if it can be folded without
// side-effects. This is an important GNU extension. See GCC PR38377
// for discussion.
这又指向讨论此问题的 gcc 的 Bugzilla bug 38377。具体来说,在 2008 年,此错误被报告为“__builtin_constant_p(t) ? t : 1 is not视为常量整数表达式”。在讨论中,注意到对于条件运算符 (?:),
是的,这是一个(记录在案的)特殊情况,需要兼容
现有的 GNU C 代码。
还有,
如果您对 C 有此错误,则 GCC 将无法引导,因为它是 GCC 在使用 GCC 构建时所依赖的 GNU C 语义的一部分。
鉴于此,该行为似乎既具体又经过深思熟虑,并且由于 gcc 本身依赖于它,因此可能是一种相当稳定的行为。
不过,关于使用非标准实现细节的所有常见警告仍然适用。如果您可以在运行时执行此操作,则 gcc 和 clang 都可以接受:
ptrdiff_t cb = (char *)(&(B&)*(C*)nullptr) - (char *)nullptr;