C++ 如何静态断言指针强制转换是微不足道的?

C++ 如何静态断言指针强制转换是微不足道的?,c++,pointers,casting,static-assert,C++,Pointers,Casting,Static Assert,假设我有以下类型: 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&l

假设我有以下类型:

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(size\u t(static\u cast)。使用
offsetof()
也是唯一有用的建议。

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

<>你的“即”是不正确的。例如,完全可以的是,<>代码>派生的的前4个字节是VTABLE指针,即使当代码> BASE< /COD>不是多态的时候,是的,如果(1)第一基子对象位于偏移0,C++编译器更容易,并且(2)vtable指针位于偏移量0处。但这两个目标本质上是不一致的,没有更好的选择


现在,第一部分可以在理论上进行测试。使用标准布局类型,在(基础,第一个成员)的
offset\u和(派生,第一个成员)的
offset\u方面没有区别
。但实际上,
的偏移量u对有趣的类型不起作用;它是UB。这个检查的全部目的是检查一个类型,所以对于非标准的布局类型,它应该会可靠地失败。

根据MSalters的建议和来自的答案,这里是我能想到的最接近的答案。它可能是gcc specific,并且需要了解基类的某些成员:

#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"

显然,这既不方便又可怕(需要知道一个成员并关闭警告).

我不想吹毛求疵,但既然精确的内存布局是一个实现细节……你为什么会对此感兴趣呢?我很确定Boost/lambda库中包含了这方面的内容。我必须查找一下,但Alexandrescu和Vandervoorde已经发布了我真正感兴趣的这个领域中的每一个技巧@Matthieu M.提出的问题是:在什么情况下,如果一个强制转换可以是微不足道的(即,如果它不是微不足道的,那么成本很可能可以忽略不计:在派生指针上添加一个常量,如果条件没有正确完成,您将在调试看起来像损坏的内存时遇到真正的麻烦)说来话长。这是一个事件系统,我想使用类型擦除来减少模板膨胀。例如,我想将类型f(T*)(对于许多不同的T)的函数指针视为f(void*),将它们存储在公共结构中。进一步的细节是复杂的。重点是有许多重复的模板化代码实际上是相同的,因为强制转换很简单(在我处理的大多数情况下,它实际上是)。我试图避免重复生成的相同代码,但请编译器检查我是否弄糟了:)@DS,您可能希望检查链接器的设置。某些链接器具有检测和合并相同函数的能力,因此,即使您的编译器正在生成大量重复函数,您正在进行的合并工作也可能是计算机可以为您做的工作。“您无法检查,因为这不是真的”。我的意思是,因为它并不总是正确的,我需要能够检查!现在,offsetof(C,a)的想法听起来很有希望,但gcc抱怨“对NULL对象的非静态数据成员'a::a'的访问无效。”事实上,gcc的投诉是一个警告,而不是一个错误。因此,当知道Base的第一个成员时,这确实起作用。这很重要。谢谢!我编辑了MSalters的评论“你不能检查,因为这不是真的”,以使我更清楚地认识到“你的I.e.不正确”.我不认为MSalters会试图说你的整个陈述是错误的。根据他的下一句话,我认为他的意思是“两个相关类型可以转换…”实际上并不等于“没有多重继承…”。