constexpr基类的地址

时间:2013-11-26 11:22:19

标签: c++ base-class constexpr c++14

使用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; 

1 个答案:

答案 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,CB1CB2都可以使用或不使用__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;