如何让整数映射到 C++ 中的类定义

How to let an integer maps to a class definition in C++?

本文关键字:定义 C++ 整数 映射      更新时间:2023-10-16

我有一些类:

class Base
{
public:
    virtual void Something() = 0;
}
class A : public Base
{
public:
    virtual void Something() { /*...*/ }
    void SpecialActionForA();
}
class B : public Base
{
public:
    virtual void Something() { /*...*/ }
    void SpecialActionForB();
}

和一个数组:

Base* MyMembers[2];
MyMembers[0] = new A;
MyMembers[1] = new B;

我想做:

A* pointer_to_a = Get(0);
B* pointer_to_b = Get(1);

有什么好方法可以实现这个Get()功能吗?


我的解决方案是:

template <typename T>
T* Get(int index)
{
    return dynamic_cast<T*>(MyMembers[index]);
}

但首先是我必须写

A* pointer_to_a = Get<A>(0)

这需要额外的<A>;

第二件事是,如果new以某种方式搞砸了,例如:

MyMembers[0] = new B;

然后Get()失败。
我想要的是一种自动机制,可以将索引 0 映射到 A。


额外详情:实际上,我从Base派生了80不同的类(它们是我的用户界面),
我需要的是让真正的类(真正的 UI)来做事。

我需要使用的功能是上述SpecialActionForA()...等。

也使用该Something(),但处于这些 UI 的初始化阶段,或由 UI 管理器系统管理的内容。

原则上

不能执行unknown* Get(int),因为它需要两个或多个具有相同名称但仅在返回值上不同的函数。C++明确禁止这样做。

您可以获得的最接近的函数是:

template <typename T> T* Get() {
  static T* instance = new T;
  return instance;
}

因此,您将能够将其用作:

A *a = Get<A>(); 
B *b = Get<B>();

通过查找参数列表来重载函数。仅通过判断返回类型无法了解您想要什么样的值。因此,编译器强制您提供额外的详细信息来实例化模板。因此,您的选择是:

Base *Get(int N) {
    return MyMembers[N];
}

A *GetA(int N) {
   return MyMembers[N];
}
B *GetB(int N) {
   return MyMembers[N];
}

template <typename T> void Get(int N, T *&item) {
   item = (T*)MyMembers[N];
}
A* pointer_to_a; 
B* pointer_to_b; 
Get(0, pointer_to_a);
Get(1, pointer_to_b);

或您自己的

template <typename T>
T* Get(int index)
{
    return dynamic_cast<T*>(MyMembers[index]);
}

在不知道您的使用场景的情况下,很难提供有用的答案,但我实际上确实相信您想要的是类型匹配。所以你要做的是将变量作为 Base 指针传递,直到我你实际上需要知道具体的底层类型,此时你对基指针的基础类型进行匹配。这与混合了 lambda 的开关语句非常相似,我将尝试提供一个示例。

...
Base* ptr = some_argument;
// Here we need the underlying type, so we'll do a match
Match(ptr)
{
    Case(A* a)
    {
        // this is invoked if ptr happened to hold an A, now do something A specific.
    }
    Case(B* b)
    {
        // this is invoked if ptr happened to hold a B, now do something B specific
    }
    Otherwise()
    {
        // this is invoked whenever none of the above are
    }
}
EndMatch;
...
这是通常解决方案

的替代方法,通常解决方案是基类中的虚拟指针和重写。请注意,类型匹配通常用于函数式语言。对于C++来说,有一个切肉刀解决方案可以实现这一目标,由Bjarne Stroustrup的博士生之一Yuriy Solodkyy提供。

简短的介绍性论文可以在这里找到;

https://parasol.tamu.edu/~yuriys/papers/OPM13EA.pdf

有关更多信息,请查看提交给 ISO 委员会的 hvis 论文;用于C++的开放式高效型开关,由Stroustup,Dos Reis和Solodkyy提供

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3449.pdf

有关这些论文背后的源代码,请访问Yuriys主页;

https://parasol.tamu.edu/~yuriys/pm/

如果你拥有的类数是真正封闭的,那么考虑使用闭合代数数据类型,就像boost::variant一样。与不受限制的开放代数数据类型相比,这些数据类型具有一些优点,因为编译器通常可以确保穷举匹配,并且封闭性质允许更优化的模式匹配实现,尽管以灵活性为代价。

另请注意,boost::variant在 C++03 上运行良好!

但是,请考虑是否真的需要模式匹配或多态调度。这两种方法都有优点和缺点,模式匹配方法的主要优点是不需要更改类,并且避免使用超特定的虚拟方法污染基类,这些方法只能在一个位置调用。