9

假设我有这些类型:

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);

这样可行。但是这些信息在编译时是已知的,所以我正在寻找一种方法来对其进行编译时断言。将上述转换为静态断言的明显方法(例如,用 g++ 4.2 替换assertBOOST_STATIC_ASSERT错误“转换为整数或枚举类型以外的类型不能出现在常量表达式中”。

便携性不是很重要。使用 gcc 扩展或 hacky 模板技巧都可以。

更新:发现之前也问过几乎一样的问题:C++,静态检测不同地址的基类?. 使用offsetof()也是那里唯一有用的建议。

4

2 回答 2

4

“我想确保两个相关类型可以相互转换而不改变地址(即没有多重继承,或者基类是派生类的第一个基类)。”

你的“ie”不正确。例如,前 4 个字节完全有可能Derived是一个 vtable 指针,即使Base不是多态的。是的,如果 (1) 第一个基本子对象在偏移量 0 处,并且 (2) vtable 指针在偏移量 0 处,C++ 编译器会更容易。但这两个目标本质上是不一致的,没有明显更好的选择。

现在,第一部分可以在理论上进行测试。offset_of(Base, first_member)对于标准布局类型,和没有区别offset_of(Derived, first_member)。但在实践中,offset_of不适用于有趣的类型;是UB。这个检查的重点是检查一个类型,所以对于非标准布局类型,它应该可靠地失败。

于 2011-04-04T08:07:45.187 回答
1

根据 MSalters 的建议和C++ 的回答,静态检测具有不同地址的基类?,这是我能想到的最接近答案的东西。它可能是 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"

显然,这既不方便又令人恐惧(需要认识成员并关闭警告)。

于 2011-04-04T17:43:22.807 回答