使用特定成员变量值创建类型的类型

Create distinct type for class with specific member variable value

本文关键字:类型 创建 变量值 成员      更新时间:2023-10-16

给定一个具有定义类型类的枚举的类,例如在以下示例中:

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.
};

有可能以某种方式定义一个特定枚举值的水果和干果的不同类型:

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

使苹果,橙色和香蕉是不同的类型,三个类干燥,干果,干bana是不同的类型。

我的问题与如何在C 中定义同一类的不同类型有点相似,除了我想明确存储有关类中的enum成员变量的信息,并为所有不同的不同类型。

最有效的方法是什么?

编辑:主要用例如下 - 在我的应用中,某些方法只能期望苹果作为输入,或者只期望橙色作为输入,而许多不关心哪种水果的方法。

将水果传递到只期望苹果的方法感觉不安全/晦涩难懂,与此同时,有许多方法不在乎哪种类型是什么类型,因此拥有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>;

您是否需要实际的基类取决于您。为某些FruitTypes。

提供模板专业可能也足够

这是您想做的吗?

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;
}

打印:

Is Apple? true
Is Orange? false
Is Banana? false

您的问题对要求很抽象。

尽管您的编辑澄清指导了一种方式

主要用例如下 - 在我的应用程序中,有些方法只期望苹果作为输入,或者只期望橙色作为输入,而许多不关心哪种水果的方法。

我正在考虑基于接口和 tag界面的完全不同的系统
(请参阅完整的实时演示)。

首先定义所有水果的常见接口:

// 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接口。
该基类是 abractive 在某种意义上,构造函数函数隐藏在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的已知实例,用于实现特定的 tag接口: 模板 taginterface* querytaginterface(ifruit* frual){ 返回Dynamic_cast(水果); }


这就是您在行动中使用所有这些的方式:

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;
    }    
}

将水果传递到只期望苹果的方法感觉不安全/晦涩难懂,与此同时,有许多方法不在乎哪种类型是什么类型,因此拥有3种不同类型的方法也不是一个好选择。

我应该注意,我仍然不明白是什么使苹果 oranges fuits 他们真的应该得到自己的类型。但是,这可能适用于多态类设计的不抽象隐喻,对具体类别的层次结构有用。