给定一个具有定义类类型的枚举的类,如下例所示:
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_;
};
和从它派生的类共享相同的枚举:
class DriedFruit : public Fruit {
public:
// Some Dried specific methods.
};
是否有可能以某种方式为每个特定的枚举值定义Fruit和DriedFruit的不同类型:
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类Apple,Orange和Banana是不同的类型,3类DriedApple,DriedOrange,DriedBanana是不同的类型。
我的问题有点类似于How to define different types for the same class in C++,除了我想在类中将类类型的信息显式存储为枚举成员变量,并为所有不同类型提供公共基类。
最有效的方法是什么?
编辑: 主要的用例如下 - 在我的应用程序中,有些方法只是期望Apple作为输入,或者只是期望Orange作为输入,而且许多方法都不关心它是什么。
将Fruit传递给只需要Apple的方法感觉不安全/模糊,同时有许多方法不关心它是哪种类型因此有3种不同的类型也不是一个好的选择。
主要工作流程如下: 然后从一些输入参数构建一个Fruit 传递它并将其作为水果处理,然后在某个时刻 如果它是Apple,可以从Fruit转换为具体的Apple类型,并进一步处理它,从那时起将其类型限制为Apple。
答案 0 :(得分:3)
最有效的方法是什么?
您可以使用非类型模板参数:
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>;
您是否需要实际的基类取决于您。为某些FruitType
提供模板特化也可能就足够了。
答案 1 :(得分:2)
这是你想做的吗?
map.UserLocationTouch = false
打印:
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;
}
答案 2 :(得分:0)
关于要求,你的问题非常抽象。
虽然您的编辑说明指明了一种方式
主要用例如下 - 在我的应用程序中,某些方法只是期望Apple作为输入,或者仅仅期望Orange作为输入,并且许多方法不关心它是什么。
我正在考虑基于接口和标记接口的完全不同的系统
(参见完整的Live Demo)。
首先为所有水果定义一个通用界面:
// 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;
}
定义标记接口以区分您的特定类型(苹果,橘子):
// Tag interfaces to distinguish (not necessarily empty)
struct IApple : public IFruit {
virtual ~IApple() {}
};
struct IOrange : public IFruit {
virtual ~IOrange () {}
};
这些应该要求隐式实现IFruit
接口。
现在您可以提供一个抽象基类,它实现IFruit
接口
这个基类是 abstract ,从某种意义上说构造函数是从public
范围隐藏的,需要由继承类构造函数调用:
// 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_; }
};
根据需要添加其他标记接口:
struct IDriedApple : public IApple {
virtual ~IDriedApple() {}
virtual int rest_humidity() const = 0;
};
现在,您可以使用非常狭窄的类定义创建具体实现:
// 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") {}
};
这里我假设的是专门用于 Apples 或 Oranges 的函数声明:
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;
}
这是一个简单的模板函数,用于查询IFruit
的已知实例,以实现特定的标记接口:
模板
TagInterface * queryTagInterface(IFruit * fruit){
return dynamic_cast(fruit);
}
这就是你如何使用所有这些:
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;
}
}
将Fruit传递给只需要Apple的方法感觉不安全/模糊,同时有许多方法不关心它是哪种类型因此有3种不同的类型也不是一个好的选择。
我应该注意到,我还是不明白是什么让 Apples 和 Oranges 让不同的 Fruits 得到他们真正应得的自己的类型。但是,这可能适用于不那么抽象的多态类设计隐喻,对于具体的类层次结构设计很有用。