C++ RTTI 注册表模式
C++ RTTI Registry Pattern
我有兴趣详细了解 C++ 中 RTTI 的优势和局限性。假设我有以下场景:
class C {};
class C0 : public C {};
class C1 : public C {};
...
void print(int id, C* c) {
if (id == 0) dynamic_cast<C0 *>(c)->print();
else if (id == 1) dynamic_cast<C0 *>(c)->print();
...
}
是否可以使用注册表模式实现上述示例?例如,使用这样的东西:
map<int, ?> registry;
void print(int id, C* c) {
registry[id](c)->print();
}
只需将 print
设为虚函数即可轻松解决此问题。那么你可以简单地拥有:
void print(C* c)
{
c->print();
}
并且它会为所有派生的 classes 做正确的事情。
但是如果你想保持 print
非虚拟,那么正如你所认识到的那样,有一个问题是像 registry[id](c)->print();
这样的东西是如何工作的。映射的值类型是编译时事实,但您希望行为有 运行 时差。
好吧,我可以想到一种方法...通过使用虚函数。您需要创建一个 class 作为 C
的包装器,派生版本与从 C
派生的类型相匹配。一些模板的使用可能会使这个处理起来比较体面。然后映射根据指向基的指针声明,但通过派生包装器填充。
但最终这需要更多的复杂性,并且提供的好处并不比首先将 print
本身虚拟化所能实现的更多。
虽然使用多态和虚方法似乎更合适,但您可以使用类似下面的方法根据 id
进行注册和调度
class PrintCaller
{
public:
template <typename T>
std::size_t register_class()
{
m.push_back([](C* c) {
auto* p = dynamic_cast<T*>(c);
if (p) {
p->print();
} else {
throw std::runtime_error("Incorrect type");
}
});
return m.size() - 1;
}
void print(std::size_t id, C* c) const {
if (id < m.size()) {
m[id](c);
} else {
throw std::runtime_error("invalid id");
}
}
private:
std::vector<std::function<void(C*)>> m;
};
我有兴趣详细了解 C++ 中 RTTI 的优势和局限性。假设我有以下场景:
class C {};
class C0 : public C {};
class C1 : public C {};
...
void print(int id, C* c) {
if (id == 0) dynamic_cast<C0 *>(c)->print();
else if (id == 1) dynamic_cast<C0 *>(c)->print();
...
}
是否可以使用注册表模式实现上述示例?例如,使用这样的东西:
map<int, ?> registry;
void print(int id, C* c) {
registry[id](c)->print();
}
只需将 print
设为虚函数即可轻松解决此问题。那么你可以简单地拥有:
void print(C* c)
{
c->print();
}
并且它会为所有派生的 classes 做正确的事情。
但是如果你想保持 print
非虚拟,那么正如你所认识到的那样,有一个问题是像 registry[id](c)->print();
这样的东西是如何工作的。映射的值类型是编译时事实,但您希望行为有 运行 时差。
好吧,我可以想到一种方法...通过使用虚函数。您需要创建一个 class 作为 C
的包装器,派生版本与从 C
派生的类型相匹配。一些模板的使用可能会使这个处理起来比较体面。然后映射根据指向基的指针声明,但通过派生包装器填充。
但最终这需要更多的复杂性,并且提供的好处并不比首先将 print
本身虚拟化所能实现的更多。
虽然使用多态和虚方法似乎更合适,但您可以使用类似下面的方法根据 id
class PrintCaller
{
public:
template <typename T>
std::size_t register_class()
{
m.push_back([](C* c) {
auto* p = dynamic_cast<T*>(c);
if (p) {
p->print();
} else {
throw std::runtime_error("Incorrect type");
}
});
return m.size() - 1;
}
void print(std::size_t id, C* c) const {
if (id < m.size()) {
m[id](c);
} else {
throw std::runtime_error("invalid id");
}
}
private:
std::vector<std::function<void(C*)>> m;
};