派生结构和指向父结构中自身的指针
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;
}
我有三个结构,一个包含属性的“父”结构,以及两个从父结构派生的结构,其中我只有方法。本质上结构的内容总是相同的,但我希望在不同情况下相同方法的不同实现。
示例代码:
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;
}