首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >cpp中模拟枚举继承的解决方案

cpp中模拟枚举继承的解决方案
EN

Stack Overflow用户
提问于 2021-09-04 01:37:14
回答 3查看 194关注 0票数 7

我知道枚举继承在c++中是不可能的,但我正在寻找适合我的情况的特定数据结构。假设我有这两个枚举:

代码语言:javascript
运行
复制
    enum Fruit { apple, orange};
    enum Drink { water, milk};

我想要这两种方法的父类,我可以在这个抽象方法中用作参数。

代码语言:javascript
运行
复制
   void LetsEat(Eatable eatable){}

它们将作为简单的开关使用,基本上我希望保持代码的干净和类型的安全。我不知道是否会被迫使用需要初始化的继承类。对于这个简单的问题来说,太多了。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-09-04 01:57:41

一般说来,enum只是打扮成int的样子。

代码语言:javascript
运行
复制
enum Fruit { apple, orange};

如果查看编译后的代码,您将发现apple将由值0表示,而orange将由值1表示。

代码语言:javascript
运行
复制
enum Drink { water, milk};

这里也会发生同样的事情。water将由值0表示,milk将由值1表示,您可以在这里看到明显的问题。

一种是稍微原始的解决方案,相当于在中国店里放牛:

代码语言:javascript
运行
复制
enum Drink { water=2, milk=3};

现在,您可以在传入int值的地方编写一些内容,并根据它的值计算传入的确切内容。

但这可能需要大量丑陋的造型,到处都是。如果将结果代码发布到Stackoverflow中,则可能会吸引更多的选票。

这将是因为在现代的后C++17世界中有更清洁的解决方案。首先,您可以切换到enum类。

代码语言:javascript
运行
复制
enum class Fruit { apple, orange};
enum class Drink { water, milk};

这获得了额外的类型安全。将Fruit分配给Drink不再那么容易了。在许多情况下,C++编译器会发出警告,声音很大。您的C++编译器将帮助您在代码中找到更多的bug。的确,这将需要更多的输入。如果在现有代码中仅使用Fruit::applewater就足够了,则始终必须在任何地方指定具有完全限定条件的枚举值,即applewater。但是,为了获得更多的类型安全代码并能够简单地声明,一些额外的类型字符是一个小代价:

代码语言:javascript
运行
复制
typedef std::variant<Fruit, Drink> Eatable;

做你一直想做的事:

代码语言:javascript
运行
复制
void LetsEat(Eatable eatable){}

一切都会像你想要的那样运作。LetsEat将接受FruitDrink作为其参数。它将需要做更多的工作,以确定什么是在std::variant,但从来没有人声称C++是容易的。

std::variant是C++库中比较复杂的模板之一,不可能在Stackoverflow上的一两段短短的段落中解释如何完整地使用它。但这是可能的,我将参考您的C++教科书,以获得如何使用此模板的完整说明。

票数 8
EN

Stack Overflow用户

发布于 2021-09-04 01:53:39

对于std::variant来说,这听起来是一个很好的用例。

代码语言:javascript
运行
复制
#include <variant>
#include <iostream>

// Define our enums
enum Fruit { Apple, Orange };
enum Drink { Water, Milk };

// An Eatable is either a Fruit or a Drink
using Eatable = std::variant<Fruit, Drink>;

void letsEat(Eatable eatable) {
  // We can use the index() method to figure out which one we have
  switch (eatable.index()) {
  case 0:
    std::cout << "It's a Fruit!" << std::endl;
    break;
  case 1:
    std::cout << "It's a Drink!" << std::endl;
    break;
  }
}

int main() {
  letsEat(Apple);
  letsEat(Water);
}

请注意,严格地说,std::variant<Fruit, Drink>不是FruitDrink的超级类型。相反,它完全是一种新的类型,但是我们通过它的构造函数从FruitDrinkstd::variant<Fruit, Drink>进行隐式转换。

如果不使用C++17,则可以使用Boost C++库中的boost::variant

票数 7
EN

Stack Overflow用户

发布于 2021-09-04 01:53:47

如果使用std::variant或更高版本,则可以使用C++17:

代码语言:javascript
运行
复制
#include <iostream>
#include <variant>
#include <type_traits>

enum Fruit { apple, orange };
enum Drink { water, milk };

using Eatable = std::variant<Fruit, Drink>;

void LetsEat(Eatable const eatable) {
    std::visit([] (auto&& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_same_v<T, Fruit>) {
            // Now use it like you would use a normal 'Fruit' variable ...
        }
        if constexpr (std::is_same_v<T, Drink>) {
            // Now use it like you would use a normal 'Drink' variable ...
        }
    }, eatable);
}

int main() {
    LetsEat(apple);
}

或者,您只需创建一个隐式可转换为任一enum类型的类:

代码语言:javascript
运行
复制
class Eatable {
    union {
        Fruit f;
        Drink d;
    } u_;
    bool has_fruit_;
public:
    Eatable(Fruit f) : has_fruit_(true) {
        u_.f = f;
    };
    Eatable(Drink d) : has_fruit_(false) {
        u_.d = d;
    };
    operator Fruit() const {
        return u_.f;
    }
    operator Drink() const {
        return u_.d;
    }
    bool has_fruit() const {
        return has_fruit_;
    }
};

然后你可以像这样使用它:

代码语言:javascript
运行
复制
void LetsEat(Eatable const eatable) {
    if (eatable.has_fruit()) {
        Fruit const f = eatable;
        switch (f) {
            case apple:
                std::cout << "Fruit: apple" << std::endl;
                break;
            case orange:
                std::cout << "Fruit: orange" << std::endl;
                break;
            default: break;
        }
    } else {
        Drink const d = eatable;
        switch (d) {
            case water:
                std::cout << "Drink: water" << std::endl;
                break;
            case milk:
                std::cout << "Drink: milk" << std::endl;
                break;
            default: break;
        }
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69051830

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档