编译时计算基类的偏移量

发布时间:2022-09-17 / 作者:清心寡欲
本文介绍了编译时计算基类的偏移量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以在编译时计算基类偏移量。当然,在运行时很容易做到这一点,因为可以利用static_cast的功能,而偏移量只是指向派生类的指针的基指针之间的差异。

我第一次尝试在编译时获取它,结果如下所示:

struct InterfaceRoot {};

struct IInterface1 : InterfaceRoot {
    virtual void MethodI1() = 0;
};

struct IInterface2 : InterfaceRoot {
    virtual void MethodI2() = 0;
};

class CClass : public IInterface1, public IInterface2 {
    virtual void MethodI1() override { /* do something */ }
    virtual void MethodI2() override { /* do something */ }     
};

int main() {

    CClass instance;

    constexpr int offsetI1 = 0; //first base class has no offset
    constexpr int offsetI2 = sizeof(IInterface1);

    //check pointer values against static_cast
    IInterface1* pI1 = reinterpret_cast(reinterpret_cast(&instance) + offsetI1);
    IInterface2* pI2 = reinterpret_cast(reinterpret_cast(&instance) + offsetI2);

    IInterface1* pI1_static_cast = static_cast(&instance);
    IInterface2* pI2_static_cast = static_cast(&instance);

    return 0;
}

这里,pI1pI1_static_cast与预期相同。然而,pI2pI2_static_cast并不相等!?

我可以通过向InterfaceRoot添加一个虚函数或将其全部省略来修复此问题。发生这种情况的原因是什么?

如果我这样设置继承树,它将使用前面提到的方法:

struct InterfaceRoot {
    virtual ~InterfaceRoot() {}
};

struct IInterface1 : InterfaceRoot {
    virtual void MethodI1() = 0;
};

struct IInterface2 : InterfaceRoot {
    virtual void MethodI2() = 0;
};

class CClass : public IInterface1, public IInterface2 {
    virtual void MethodI1() override { /* do something */ }
    virtual void MethodI2() override { /* do something */ }
};

有人知道这是为什么吗?顺便说一句,我正在使用Visual Studio 2017。有没有其他方法可以在编译时实现我的目标,或者我最好在运行时计算ofset并具有较小的运行时开销?

编辑:

工作的运行时实现可能如下所示:

template
inline int CalcBaseOffset() {
    const static int s_off = (reinterpret_cast(static_cast(reinterpret_cast(0x10000000))) - reinterpret_cast(0x10000000));
    return s_off;
};

int main() {
    //...

    int offsetI1_RT = CalcBaseOffset();
    int offsetI2_RT = CalcBaseOffset();

    // add offsets to pointer like in the code sample above

}

此方法可产生准确的结果,但以小的运行时开销为代价(如果无法在编译时计算偏移量以消除此开销,则运行时开销是可接受的)。

推荐答案

编译器可能会在基类IInterface1IInterface2之间的CClas中引入填充。

基本X:sizeof(CClas)>;=sizeof(IInterface1)+sizeof(IInterface2)

然后,以下语句可能会返回错误的地址:

IInterface2*Pi2= Reinterpret_cast<;IInterface2*>;(reinterpret_cast<;char*>;(&;instance)+ 偏移量I2

X请注意,如果基类没有virtual成员函数并且由于Empty Base Optimization而为空(即没有数据成员),则这可能不成立。

这篇关于编译时计算基类的偏移量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持吉威生活!



[英文标题]Calculate offset of base class at compile time


声明:本媒体部分图片、文章来源于网络,版权归原作者所有,如有侵权,请联系QQ:330946442删除。