使用clang 3.4(trunk),有没有办法用常量表达式计算基类的位移?
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;
答案 0 :(得分:5)
是的,可以使用常量表达式计算基类的位移,但它根本不可移植。您可以使用clang也支持的鲜为人知但documented gcc扩展名。它涉及在与运营商__builtin_constant_p
:
?:
的使用
#define CB (&(B&)*(C*)nullptr)
constexpr auto c_b_address = __builtin_constant_p CB ? CB : CB;
请注意,我刚刚使用宏CB来明确发生了什么。当然,这也可以通过多次重复表达来完成。顺便说一下,我首先了解了这个包含有用背景信息的技巧in this question。
正如您可能已经理解的那样,基本问题是reinterpret_cast
表达式中不允许使用constexpr
或等效的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在评论中非常合理地问道,&#34;你有一个gcc或clang参考,说他们支持这种行为吗?&#34;我要扩大这个范围以询问&#34;存在什么文件表明这是故意的,而不仅仅是一个奇怪的副作用?&#34;毕竟,如果有人真的要使用它,那么有一些迹象表明它将来可能会继续存在,这将是一件好事。本节试图解决这个问题。
对于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年这个错误被报告为&#34; __ builtin_constant_p(t)? t:1不被视为常数整数表达式&#34;。在讨论中,它注意到对于条件运算符(?:
),
是的,这是一个(文件化的)需要兼容的特殊情况 现有的GNU C代码。
而且,
如果你对C做错了,那么GCC将无法引导,因为它是GCC在使用GCC构建时依赖的GNU C语义的一部分。
鉴于此,似乎行为既具体又刻意,并且因为gcc本身依赖于它,可能是一种相当稳定的行为。
尽管如此,关于使用非标准实现细节的所有常见警告都适用。如果你可以在运行时执行此操作,那么gcc和clang都可以接受:
ptrdiff_t cb = (char *)(&(B&)*(C*)nullptr) - (char *)nullptr;