使用标记联合的C++多态性

zi8p0yeb  于 2023-05-30  发布在  其他
关注(0)|答案(2)|浏览(136)

我在玩C++中的多态性,我发现了std::variant。然而,它似乎不是很高的性能,也不是编码友好,在大多数情况下,你必须使用虚拟基类。
所以我“重新发明”了标记联合,它允许你存储多个类,并允许你几乎直接根据存储的类型调用它们的成员函数。
假设我有以下包含具有相同签名的方法的类:

struct A {
    void print() { printf("A"); }
    char id() { return 'A'; }
};

struct B {
    void print() { printf("B"); }
    char id() { return 'B'; }
};

struct C {
    void print() { printf("C"); }
    char id() { return 'C'; }
};

struct D {
    void print() { printf("D"); }
    char id() { return 'D'; }
};

现在我有一个tagged_union类,它包含所有类的union沿着存储的类型id,并使用内部switch实现它们的方法(不是真实的的用例,只是一个例子,我真的在光线跟踪器中使用它来存储不同的基本网格):

struct tagged_union{
    tagged_union(){}
    tagged_union(A val): a(val), type(0) {}
    tagged_union(B val): b(val), type(1) {}
    tagged_union(C val): c(val), type(2) {}
    tagged_union(D val): d(val), type(3) {}
    void print() {
        switch(type){
            case 0: a.print(); break;
            case 1: b.print(); break;
            case 2: c.print(); break;
            case 3: d.print(); break;
            default: break;
        }
    }
    char id() {
        switch(type){
            case 0: return a.id(); break;
            case 1: return b.id(); break;
            case 2: return c.id(); break;
            case 3: return d.id(); break;
            default: return 0;
        }
    }
    private:
    union{
        A a;
        B b;
        C c;
        D d;
    };
    int type;
};

现在我的问题是是否可以使定义并集的过程自动化(例如,变量模板+递归定义成员),并在运行时在成员函数中选择正确的存储类型,而不必为每个成员函数写出switch?
我为任何语法错误和不正确的术语道歉,因为我不是一个母语人士。
编辑:我已经清楚了,你不必使用virtual来从variant调用多态函数,但是我仍然出于好奇而坚持最初的问题。

cl25kdpy

cl25kdpy1#

因此,存储在一个变体中的四个类将是std::variant<A, B, C, D>
例如,创建这些对象的向量并调用每个对象的print成员函数的代码可能如下所示:

int main() {
    using T = std::variant<A, B, C, D>;

    std::vector<T> objects = { A(), B(), C(), D() };

    for (auto const& obj : objects)
        std::visit([](auto o) { o.print(); }, obj);
}

使用每一个的id()将是类似的,除了(当然)用o.id()替换o.print()
不确定在什么情况下可以将虚拟继承与std::variant结合使用。我已经使用variant相当多的时间了,我非常确定所涉及的类型都没有使用过虚继承(甚至没有使用虚函数的普通继承)。

w1jd8yoj

w1jd8yoj2#

您可以创建模板方法来执行一次分派(或const版本的两次分派):

template <typename F>
decltype(auto) call(F f) const
{
    switch (type){
        case 0: return f(a);
        case 1: return f(b);
        case 2: return f(c);
        case 3: return f(d);
    }
    throw std::runtime_error ("Unreachable code");
}

void print() const {
    call([](const auto& u){ u.print(); });
}
char id() const {
    return call([](const auto& u){ return u.id(); });
}

Demo

相关问题