派生结构和指向父结构中自身的指针

derived structs and pointer to self in parent struct

我有三个结构,一个包含属性的“父”结构,以及两个从父结构派生的结构,其中我只有方法。本质上结构的内容总是相同的,但我希望在不同情况下相同方法的不同实现。

示例代码:

struct Unit {

    int id;
    Unit* next;
};

struct CUnit: Unit {

    bool some_complicated_condition(); // client impl
};

struct SUnit: Unit {

    bool some_complicated_condition(); // server impl
};

CUnit* get_client_unit_by_id(int id) {

    CUnit* unit = g_client_units;
    
    while (unit) {
    
        if (unit->id == id && unit->some_complicated_condition()) {
            return unit;
        }
        
        unit = unit->next;
    }
    
    return nullptr;
}

当然在编译时,出现以下错误:
error C2440: '=': cannot convert from 'Unit *' to 'CUnit *'
note: Cast from base to derived requires dynamic_cast or static_cast

我可以像这样投射 unit->next(CUnit*)unit->next,但我尽量避免这种情况。我已经搜索过但找不到答案。仅仅是因为结构的内容在两个实现之间是相同的,我希望能够按原样使用它们而无需强制转换。

所以我想知道,有没有什么方法可以重新定义我的结构,以允许开箱即用?

回答您最初的问题(似乎没有人愿意真正回答 - 而只是给您 关于如何编写代码的意见):是的,您可以做什么你想做的,它涉及通过你想要存储在下一个指针中的模板化单元:https://godbolt.org/z/qrrMfc7PM

请注意,此模式非常常见,因此有一个名称:Curiously Recurring Template Pattern,或 CRTP。阅读所有关于它的内容。 https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

template < class t_TyNext >
struct Unit {

    int id;
    t_TyNext * next;
};

struct CUnit: Unit< CUnit > {

    bool some_complicated_condition(); // client impl
};

struct SUnit: Unit< SUnit > {

    bool some_complicated_condition(); // server impl
};

CUnit* g_client_units;

CUnit* get_client_unit_by_id(int id) {

    CUnit* unit = g_client_units;
    
    while (unit) {
    
        if (unit->id == id && unit->some_complicated_condition()) {
            return unit;
        }
        
        unit = unit->next;
    }
    
    return nullptr;
}

但是,您可能有正当理由不想模板化基本 Unit 结构 - 它可能会导致生成一堆您不想拥有的代码。在那种情况下,我建议使用下面的解决方案,它只是使用一种名为“封装”的可靠技术隐藏基本 class 实现。然后不是直接访问成员(这是糟糕的 C++ 风格 有时 取决于上下文),而是使用强制转换访问器访问下一个指针。这有助于保持代码“干净”,正如您建议的那样是您的目标。

struct Unit {
    int id;
    Unit* next;
};

struct CUnit: protected Unit 
{
    using Unit::id;
    bool some_complicated_condition(); // client impl
    CUnit * GetNext() { return (CUnit *)Unit::next; }
};

struct SUnit: protected Unit {
    using Unit::id;
    bool some_complicated_condition(); // server impl
    SUnit * GetNext() { return (SUnit *)Unit::next; }
};

CUnit* get_client_unit_by_id(int id) {

    CUnit* unit = g_client_units;
    
    while (unit) {
    
        if (unit->id == id && unit->some_complicated_condition()) {
            return unit;
        }
        
        unit = unit->GetNext();
    }
    
    return nullptr;
}