Create distinct type for class with specific member variable value
给定一个具有一些枚举的类,该枚举定义了该类的类型,如以下示例所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Fruit { public: enum class FruitType { AppleType = 0, OrangeType = 1, BananaType = 2, }; Fruit(FruitType type) : type_(type) {} FruitType fruit_type() const { return type_; } private: FruitType type_; }; |
以及派生自该类的类具有相同的枚举:
1 2 3 4 | class DriedFruit : public Fruit { public: // Some Dried specific methods. }; |
是否可以通过每种特定的枚举值以某种方式为Fruit和DryFruit定义不同的类型:
1 2 3 4 5 6 | class Apple // Fruit with FruitType = AppleType class Orange // Fruit with FruitType = OrangeType class Banana // Fruit with FruitType = BananaType class DriedApple // DriedFruit with FruitType = AppleType class DriedOrange // DriedFruit with FruitType = OrangeType class DriedBanana // DriedFruit with FruitType = BananaType |
因此3类苹果,橙和香蕉是不同的类型,而3类苹果干,桔子,香蕉干是不同的类型。
我的问题有点类似于如何在C ++中为同一类定义不同的类型,不同之处在于,我想将有关类类型的信息显式存储为类中的枚举成员变量,并为所有不同的类型提供一个通用的基类。
最有效的方法是什么?
编辑:
主要用例如下-在我的应用程序中,有些方法只希望将Apple作为输入,或者只希望将Orange作为输入,并且许多方法都不关心它是哪种水果。
将Fruit传递给仅期望Apple的方法是不安全/晦涩的,与此同时,许多方法并不关心它是哪种类型,因此拥有3种不同的类型也不是一个好选择。
主要工作流程如下:
根据一些输入参数构建一个Fruit,然后
将其传递并作为水果处理,然后在某个时候
如果是Apple,则从Fruit转换为具体的Apple类型,然后进一步处理,从那时起将其类型限制为Apple。
What would be the most efficient way to do that?
您可以使用非类型模板参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | enum class FruitType { AppleType = 0, OrangeType = 1, BananaType = 2, }; template <FruitType F> class Fruit { public: FruitType fruit_type() const { return F; } }; using Apple = Fruit<FruitType::AppleType>; using Banana = Fruit<FruitType::BananaType>; |
是否需要实际的基类取决于您。为某些
这是您想做的吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | enum class FruitType { AppleType = 0, OrangeType = 1, BananaType = 2, }; class Fruit { public: virtual FruitType fruit_type() const = 0; }; class Apple: public Fruit { public: FruitType fruit_type() const override { return FruitType::AppleType; } }; class Orange : public Fruit { public: FruitType fruit_type() const override { return FruitType::OrangeType; } }; class Banana : public Fruit { public: FruitType fruit_type() const override { return FruitType::BananaType; } }; int main() { Fruit *somefruit = new Apple; std::cout <<"Is Apple?" << std::boolalpha << (somefruit->fruit_type() == FruitType::AppleType) << std::endl; std::cout <<"Is Orange?" << std::boolalpha << (somefruit->fruit_type() == FruitType::OrangeType) << std::endl; std::cout <<"Is Banana?" << std::boolalpha << (somefruit->fruit_type() == FruitType::BananaType) << std::endl; return 0; } |
印刷品:
1 2 3 | Is Apple? true Is Orange? false Is Banana? false |
关于需求,您的问题非常抽象。
尽管您编辑后的澄清说明了一种方法
The main use case is as follows - in my applications, there are certain methods that only expect Apple as an input, or only expect Orange as an input, and many methods that do not care which fruit it is.
我正在考虑一个基于接口和标签接口的完全不同的系统
(请参阅完整的实时演示)。
首先为所有水果定义一个公共接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // A basic interface common for all fruits struct IFruit { virtual ~IFruit() {} virtual std::string category() const = 0; virtual std::string common_name() const = 0; virtual std::string botanical_name() const = 0; }; // An overload for the output operator is just nifty std::ostream& operator<<(std::ostream& os, const IFruit& fruit) { os <<"Category :" << fruit.category() << std::endl; os <<"Common Name :" << fruit.common_name() << std::endl; os <<"Botanical Name :" << fruit.botanical_name() << std::endl; return os; } |
定义标签接口以区分您的特定类型(苹果,橙子):
1 2 3 4 5 6 7 8 | // Tag interfaces to distinguish (not necessarily empty) struct IApple : public IFruit { virtual ~IApple() {} }; struct IOrange : public IFruit { virtual ~IOrange () {} }; |
这些应该要求隐式实现
现在,您可以提供一个实现
从构造器函数对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Abstract base class implementation template<class TagInterface> class FruitBase : public TagInterface { protected: std::string category_; std::string common_name_; std::string botanical_name_; FruitBase ( const std::string& category , const std::string& common_name , const std::string botanical_name) : category_(category), common_name_(common_name) , botanical_name_(botanical_name) {} public: virtual ~FruitBase () {} virtual std::string category() const { return category_; } virtual std::string common_name() const { return common_name_; } virtual std::string botanical_name() const { return botanical_name_; } }; |
根据需要添加其他标签接口:
1 2 3 4 | struct IDriedApple : public IApple { virtual ~IDriedApple() {} virtual int rest_humidity() const = 0; }; |
现在,您可以使用非常狭窄的类定义来创建具体的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Concrete apples struct Boskop : public FruitBase<IApple> { public: Boskop() : FruitBase("Apples","Boskop","Malus domestica 'Belle de Boskoop'") {} }; struct Braeburn : public FruitBase<IApple> { public: Braeburn() : FruitBase("Apples","Braeburn","Malus domestica") {} }; // Concrete oranges struct Valencia : public FruitBase<IOrange> { public: Valencia() : FruitBase("Oranges","Valencia","Citrus × sinensis") {} }; struct Navel : public FruitBase<IOrange> { public: Navel() : FruitBase("Oranges","Navel","Citrus × sinensis") {} }; |
我假设您的函数声明专门用于仅使用Apple或Oranges:
1 2 3 4 5 6 7 8 9 | void aFunctionThatTakesOnlyApples(IApple& anApple) { std::cout <<"This is an apple:" << std::endl; std::cout << anApple; } void aFunctionThatTakesOnlyOranges(IOrange& anOrange) { std::cout <<"This is an orange:" << std::endl; std::cout << anOrange << std::endl; } |
这是一个简单的模板函数,用于查询
模板
TagInterface * queryTagInterface(IFruit * fruit){
返回dynamic_cast(fruit);
}
这就是您如何实际使用所有这些方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | int main() { std::vector<std::unique_ptr<IFruit>> allFruits; allFruits.push_back(std::make_unique<Boskop>()); allFruits.push_back(std::make_unique<Braeburn>()); allFruits.push_back(std::make_unique<Valencia>()); allFruits.push_back(std::make_unique<Navel>()); for(auto& fruit : allFruits) { if(IApple* anApple = queryTagInterface<IApple>(fruit.get())) { aFunctionThatTakesOnlyApples(*anApple); } if(IOrange* anOrange = queryTagInterface<IOrange>(fruit.get())) { aFunctionThatTakesOnlyOranges(*anOrange); } std::cout <<"-----------------------------------------------" << std::endl; } } |
It feels unsafe/obscure to pass Fruit to method that only expects Apple, at the same time there are many methods that do not care which type it is so having 3 distinct types is not a good option either.
我应该注意的是,我仍然不明白是什么使苹果和橙子与它们真正应有的水果不同。但是,这可能适合于多态类设计的抽象隐喻,对于具体的类层次结构设计很有用。