简体   繁体   English

如何在 C++ 中为包含多种类型之一的包装器 class 实现多态性?

[英]How can I implement polymorphism in C++ for a wrapper class that will hold one of several types?

I am an accomplished C programmer, and I have written an assembler and VM in C ( https://github.com/chucktilbury/assembler (mostly working but not complete)) and I am thinking of porting it to C++ so I can use the STL for obvious reasons. I am an accomplished C programmer, and I have written an assembler and VM in C ( https://github.com/chucktilbury/assembler (mostly working but not complete)) and I am thinking of porting it to C++ so I can use STL 原因很明显。 This is a hobby project with no expectations of actually being useful to anyone.这是一个爱好项目,不期望对任何人真正有用。

My VM has a notion of a Value, which can have exactly one of several types.我的虚拟机有一个值的概念,它可以是几种类型中的一种。 I have implemented it using a struct with a union embedded in it.我已经使用一个嵌入了联合的结构来实现它。 This has led to a bunch of huge macros to do simple things like addition and comparisons.这导致了一堆巨大的宏来做简单的事情,比如加法和比较。

What I would like to do is have a base class that can be used to reference a Value and child classes that implement the specific type so that I don't have to spell out exactly how to retrieve the data that the Value implements.我想做的是有一个基础 class 可用于引用实现特定类型的值和子类,这样我就不必详细说明如何检索值实现的数据。

In other words, This is what I have:换句话说,这就是我所拥有的:

    ...
    switch(left_val->type) {
        ...
        case FLOAT:
            switch(right_val->type) {
            ...
                case INTEGER:
                    result->type = FLOAT;
                    result->data.float_val = left_val->data.float_val + right_val->data.int_val;
                    break;
            ...
            }
        ...
    }

What I would like to see:我想看到的:

  result->add(left_val, right_val); // over-simplified, I know....

Obviously, I can do this like I did it in C, and that's my knee-jerk reaction.显然,我可以像在 C 中那样做,这是我的下意识反应。 But I feel that I am missing some major point of how this should work in C++.但我觉得我错过了在 C++ 中应该如何工作的一些要点。

As I understand it, there is no way to achieve what I want because C++ (like C) is a statically typed language.据我了解,没有办法实现我想要的,因为 C++(如 C)是一种静态类型语言。 I may as well just encapsulate the ugly in a single class and just pass around Value(s) as I am currently doing in C.我也可以将丑陋的东西封装在一个 class 中,然后像我目前在 C 中所做的那样传递值。

Is there general agreement with that statement?是否普遍同意这种说法? Or am I totally wrong?还是我完全错了?

I suggest using a std::variant and std::visit to reduce the boilerplate.我建议使用std::variantstd::visit来减少样板。

Example:例子:

#include <cstdint>
#include <variant>

using INTEGER = std::intmax_t;
using FLOAT = long double;

using Value = std::variant<INTEGER, FLOAT>;

auto operator+(const Value& lhs, const Value& rhs) {
    return std::visit([](auto&& l, auto&& r) -> Value { return l + r; }, lhs, rhs);
}

Now adding two INTEGER s would produce a variant holding an INTEGER .现在添加两个INTEGER将产生一个包含INTEGERvariant Involving a FLOAT in the addition would make the result FLOAT , just like in the normal rules of the language.在加法中包含FLOAT将使结果FLOAT ,就像在语言的正常规则中一样。

Example usage:示例用法:

int main() {
    Value i = 10;
    Value f = 3.14159;

    auto r = i + f;
}

Demo演示

A C++20 version (kindly provided by user17732522):一个 C++20 版本(由 user17732522 友情提供):

auto operator+(const Value& lhs, const Value& rhs) {
    return std::visit<Value>(std::plus{}, lhs, rhs);
}

I have something very similar written in C++.我用 C++ 写了一些非常相似的东西。 I am assuming you have no expectation of performance.我假设您对性能没有期望。 Here are some keypoints that might help:以下是一些可能有帮助的关键点:

You will need a way to store the type information for every piece of data.您将需要一种方法来存储每条数据的类型信息。 Instead of storing data type in an enum, you should use a class for this purpose.为此,您应该使用 class 而不是将数据类型存储在枚举中。 If you want to be able to support embedding additional types, your type system should be robust.如果您希望能够支持嵌入其他类型,那么您的类型系统应该是健壮的。 Since I was aiming for an object oriented language, I have included data members and member functions (including operators and constructors) for types.由于我的目标是面向 object 的语言,因此我已经包含了类型的数据成员和成员函数(包括运算符和构造函数)。

If you want to go built-in types only, you may register objects/classes to handle operators per type.如果您只想 go 内置类型,您可以注册对象/类来处理每种类型的运算符。 Thus, once you have obtained a piece of data, you can get its type and invoke the necessary function.因此,一旦您获得了一条数据,您就可以获取它的类型并调用必要的 function。 You may do this system using polymorphism but everytime you copy a the data, you will also copy the type.您可以使用多态性来执行此系统,但每次复制数据时,您也会复制类型。 Therefore, you need to have a function that will clone your object properly.因此,您需要一个 function 来正确克隆您的 object。 You could also go simpler route, but this will take more memory.您也可以使用 go 更简单的路线,但这需要更多的 memory。

Type IntType() {
    Type t;
    t.addition = &add<int>;
    //...
    return t;
}

You can also create only one IntType and then distribute pointers of it for each data.您也可以只创建一个 IntType,然后为每个数据分配它的指针。 Something similar to a vftable.类似于 vftable 的东西。

To actually store data, I have used my Any structure, it is very similar to std::any (I wrote this around 2014).为了实际存储数据,我使用了 Any 结构,它与 std::any 非常相似(我在 2014 年左右写了这个)。 You may use std::any for the data store.您可以将 std::any 用于数据存储。 You have to be careful about types that cannot be copied.您必须小心无法复制的类型。

If you want to see the project yourself, it is publically available at here .如果您想亲自查看该项目,可在此处公开获取。 You should get 4.x-dev branch.你应该得到 4.x-dev 分支。 The system is in Source/Scripting.系统在源代码/脚本中。 Good luck.祝你好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM