1

How does one enforce a derived class to have member data of a specific derived type.

i.e.,

class Project {
  public:
    int projdata;
};

class Article: public Project {
};

class Building: public Project {
};

class Emplooyee {
  public:
    std::vector<Project> projs;
}

class Architect: public Employee {
};

class Writer: public Employee {
};

How do I now enforce that Architect objects only have projects of type Building, while Novelist only have projects of type Article? i.e., I want to have something like

class Architect: public Employee {
  public:
    std::vector<Building> projs;
};

and

class Novelist: public Employee {
  public:
    std::vector<Article> projs;
};

I could also store pointers to projects and then store cast them into the correct type. Is there a technique or design pattern to enforce such corresponding inheritance rules on members of derived classes?

highBandWidth
  • 16,751
  • 20
  • 84
  • 131
  • You can just make Employee a template with the project subtype as a type parameter. Or keep Employee and have an intermediate EmployeeImpl template to automate the casting. – Useless Jul 02 '19 at 17:21
  • This is a massive anti-pattern in OOP. Why do you care what _data_ the class stores? What, specifically, do you want to do, and why can't it be a member function instead of a member variable? – Nic Jul 02 '19 at 17:24

2 Answers2

3

A compile time solution is to make the base a template:

template<class Proj>
class Emplooyee {
  public:
    std::vector<Proj> projs;
}

class Architect: public Employee<Building> {};

class Writer: public Employee<Article> {};

Additionally, you can add a one additional non-template base so that Architect and Writer are part of same hierarchy, but that non-template base cannot deal with the projs member.

If a template not an option, then you must rely on runtime checks. For that, Project must be a polymorphic type, and you must use typeid or dynamic_cast, to enforce the invariant. And you must use indirection to store the Project's in the first place. std::vector<Project> cannot store any Building nor Article objects because it only stores Project objects only

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    Also template argument could be constrained at compile time, e.g. required to be derived from `Project` (ref: https://stackoverflow.com/a/22934960/1075282) – Renat Jul 02 '19 at 17:32
0

Like you mentioned, you could store polymorphic pointers in the base class:

class Employee {
  public:
    std::vector<Project*> projs;
}

And use dynamic_cast to downcast them:

dynamic_cast<Building*>(projs[i])->doSomething();

But I wouldn't recommend this approach(unless necessary) since this will require you to manage the memory behind those pointers. (Which of course can be off-loaded to std::unique_ptr for example.)

Unless you require the Employee to be a polymorphic class, far simpler approach would be to use a class template

template <typename T>
class Employee {
public:
    std::vector<T> projs;
}

which can be used like so:

class Architect : public Employee<Building> {
};

Architect architect;
architect.projs.push_back(Building());
Lehdari
  • 1
  • 2