假设我有这些类型:
struct A {
int a;
};
struct B {
int b;
};
struct C : public A, public B {
int c;
};
C*
指针可以强制转换为A*
指针,而根本不调整实际地址。但是当C*
转换为B*
时,值必须更改。我想确保我所拥有的两个相关类型可以在不改变地址的情况下相互转换(即,没有多重继承,或者基类是派生类的第一个基础)。这可以在运行时检查,例如像这样
assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000);
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000);
有效。但是这个信息在编译时是已知的,所以我正在寻找一种方法来对它进行编译时断言。将上面的内容转换为静态断言的明显方法(例如,用assert
替换BOOST_STATIC_ASSERT
会给出错误“对于除了整数或枚举类型以外的类型的强制转换不能出现在常量表达式中” g ++ 4.2。
便携性不是很重要。使用gcc扩展或hacky模板技巧都可以。
更新:发现以前几乎同样的问题:C++, statically detect base classes with differing addresses?。使用offsetof()
也是唯一有用的建议。
答案 0 :(得分:4)
“我想确保两个相关类型可以相互转换而不需要更改地址(即没有多重继承,或者基类是派生类的第一个基础)。”
你的“ie”是不对的。例如,Derived
的前4个字节完全有可能是vtable指针,即使Base
不是多态的。是的,如果(1)第一个基础子对象位于偏移0处,并且(2)vtable指针位于偏移0处,则C ++编译器更容易。但是这两个目标本质上是不一致的,并且没有明显更好的选择。
现在,第一部分可以在理论上进行测试。使用标准布局类型,offset_of(Base, first_member)
和offset_of(Derived, first_member)
没有区别。但在实践中,offset_of
不适用于有趣的类型;这是UB。此检查的重点是检查类型,因此对于非标准布局类型,它应该可靠地失败。
答案 1 :(得分:1)
根据MSalters的建议以及C++, statically detect base classes with differing addresses?的回答,这是我能提出的答案中最接近的答案。它可能是特定于gcc的,并且需要知道基类的某些成员:
#pragma GCC diagnostic ignored "-Winvalid-offsetof" // To suppress warning.
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a));
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b));
#pragma GCC diagnostic warn "-Winvalid-offsetof"
显然这既不方便又可怕(要求了解会员并关闭警告)。