Create a separate type for a class with a specific member variable value

For a class that has some enumeration that defines the type of class, as in the following example:

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

and a class derived from it that has the same enumeration:

class DriedFruit : public Fruit {
 public:
  // Some Dried specific methods.
};

Is there any way to define different types for Fruit and DriedFruit with each of the specific enumeration values:

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

so 3 classes Apple, Orange and Banana are different types, and 3 classes DriedApple, DriedOrange, DriedBanana are different types.

My question is somewhat similar to How to define different types for the same class in C ++ , except that I want to explicitly store class type information as an enum member variable in the class and have a common base class for all different types.

?

EDIT: : , Apple , , , , , .

, / Fruit , Apple, , , , 3 .

: , , - Apple, Fruit Apple, , Apple .

+4
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 s.

+3

, ?

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
+2

.

: , Apple , , , , , .

,
(. 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.
, 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") {}
};

, , , :

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


:

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 seems to me that it is unsafe / unclear to pass Fruit to a method that only Apple expects, at the same time there are many methods that do not care about what type it has, so having 3 different types is also not a good option.

I should note that I also still do not understand what makes Apples and Oranges different fruits, which they really deserve their own types. But it’s good, which may be appropriate for less abstract design metaphors for polymorphic classes and useful for concrete constructions of the class hierarchy.

0
source

Source: https://habr.com/ru/post/1690038/


All Articles