我知道枚举继承在c++中是不可能的,但我正在寻找适合我的情况的特定数据结构。假设我有这两个枚举:
    enum Fruit { apple, orange};
    enum Drink { water, milk};我想要这两种方法的父类,我可以在这个抽象方法中用作参数。
   void LetsEat(Eatable eatable){}它们将作为简单的开关使用,基本上我希望保持代码的干净和类型的安全。我不知道是否会被迫使用需要初始化的继承类。对于这个简单的问题来说,太多了。
发布于 2021-09-04 01:57:41
一般说来,enum只是打扮成int的样子。
enum Fruit { apple, orange};如果查看编译后的代码,您将发现apple将由值0表示,而orange将由值1表示。
enum Drink { water, milk};这里也会发生同样的事情。water将由值0表示,milk将由值1表示,您可以在这里看到明显的问题。
一种是稍微原始的解决方案,相当于在中国店里放牛:
enum Drink { water=2, milk=3};现在,您可以在传入int值的地方编写一些内容,并根据它的值计算传入的确切内容。
但这可能需要大量丑陋的造型,到处都是。如果将结果代码发布到Stackoverflow中,则可能会吸引更多的选票。
这将是因为在现代的后C++17世界中有更清洁的解决方案。首先,您可以切换到enum类。
enum class Fruit { apple, orange};
enum class Drink { water, milk};这获得了额外的类型安全。将Fruit分配给Drink不再那么容易了。在许多情况下,C++编译器会发出警告,声音很大。您的C++编译器将帮助您在代码中找到更多的bug。的确,这将需要更多的输入。如果在现有代码中仅使用Fruit::apple和water就足够了,则始终必须在任何地方指定具有完全限定条件的枚举值,即apple和water。但是,为了获得更多的类型安全代码并能够简单地声明,一些额外的类型字符是一个小代价:
typedef std::variant<Fruit, Drink> Eatable;做你一直想做的事:
void LetsEat(Eatable eatable){}一切都会像你想要的那样运作。LetsEat将接受Fruit或Drink作为其参数。它将需要做更多的工作,以确定什么是在std::variant,但从来没有人声称C++是容易的。
std::variant是C++库中比较复杂的模板之一,不可能在Stackoverflow上的一两段短短的段落中解释如何完整地使用它。但这是可能的,我将参考您的C++教科书,以获得如何使用此模板的完整说明。
发布于 2021-09-04 01:53:39
对于std::variant来说,这听起来是一个很好的用例。
#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>不是Fruit或Drink的超级类型。相反,它完全是一种新的类型,但是我们通过它的构造函数从Fruit和Drink到std::variant<Fruit, Drink>进行隐式转换。
如果不使用C++17,则可以使用Boost C++库中的boost::variant。
发布于 2021-09-04 01:53:47
如果使用std::variant或更高版本,则可以使用C++17:
#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类型的类:
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_;
    }
};然后你可以像这样使用它:
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;
        }
    }
}https://stackoverflow.com/questions/69051830
复制相似问题