6

使用clang 3.4(主干),有没有办法用常量表达式计算基类的位移?

struct A { int a; };
struct B { int b; };

struct C: A, B {};

// cannot access base class of null pointer:
constexpr auto c_b_address = /*(size_t)*/ &(B&) *(C*)nullptr; 
4

1 回答 1

5

的,可以使用常量表达式计算基类的位移,但它根本不是可移植的。您可以使用一个鲜为人知但记录在案的gcc 扩展,它也受 clang 支持。它涉及__builtin_constant_p与运算符一起使用时的使用?:

#define CB (&(B&)*(C*)nullptr)
constexpr auto c_b_address = __builtin_constant_p CB ? CB : CB;

请注意,我刚刚使用宏 CB 来说明发生了什么。当然,这也可以通过多次重复表达式来完成。顺便说一句,我首先在这个问题中了解到这个技巧,其中包含有用的背景信息。

正如您可能已经理解的那样,基本问题是表达式中既不允许 areinterpret_cast也不允许等效的 C 样式转换。constexpr奇怪的是,C 风格的演员表(如上)被接受,但 a 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,引用是源代码本身,其中函数中的注释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错误 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;
于 2014-08-06T16:37:25.330 回答