xetra11
xetra11

Reputation: 8885

How to create a vector for base class generic types to use concrete type elements

If sb. can change the title to something more understandable I'd be very thankful

This is my current implementation:

std::vector<Zone<sf::CircleShape>*> allZones;                                          
std::vector<Zone<sf::CircleShape>*> estates = initializer->initVillageEstates();       
std::vector<Zone<sf::CircleShape>*> communityAreas = initializer->initCommunityAreas();

I'd love to have something like this:

std::vector<Zone<sf::Shape>*> allZones;                                          
std::vector<Zone<sf::CircleShape>*> estates = initializer->initVillageEstates();       
std::vector<Zone<sf::RectangleShape>*> communityAreas = initializer->initCommunityAreas();

Where CircleShape and RectangleShape derive from the base class Shape. I think that would be possible for a vector if this would be like the generic type for the vector and not the generic type of the generic type of the vector.

One solution that comes in mind is that I make Zone not a template class but like ZoneShape and ZoneCircle : public ZoneShape, ZoneRectangle : public ZoneShape.

That way I could to something like this:

std::vector<ZoneShape*> allZones;                                          
std::vector<ZoneCircle*> estates = initializer->initVillageEstates();       
std::vector<ZoneRectangle*> communityAreas = initializer->initCommunityAreas();

I think that would work but I kinda found the template way more clean for my purpose. So I have to figure out how this might work with that.

Upvotes: 2

Views: 140

Answers (1)

elucent
elucent

Reputation: 36

To answer your overall question, there's no C++ way to automatically make templates follow the inheritance rules of their type parameters (some other languages like Scala will let you do this, but they're also generally not working with value types). As far as C++ is concerned, a vector of type A is a completely separate type from a vector of type B, even though they're both descended from the same template.

It sounds like you realize that, though. As far as alternatives go, making separate ZoneShape classes for every single shape you might want to hypothetically add is tedious (and violates DRY), so let's not do that. But if plain old templates don't support inheritance the way you want, and making more class inheritance is too repetitive, you could make use of a bit of both worlds:

class ZoneShape {
    // Not sure what your Zone class needs to do,
    // but include your functionality based on the
    // base Shape class here.
public:
    virtual void setShape(Shape* shape) = 0;
    virtual Shape* getShape() = 0;
};

template<typename T>
class Zone : public ZoneShape {
protected:
    // Implemented versions of the base type.
    void setShape(Shape* shape) override;
    Shape* getShape() override;

public:
    // Specific to your specific type.
    void setShape(T* t);
    T* getSpecificShape(); // EDIT: This was originally "T* getShape()", 
                           // which cannot be overloaded.
};

std::vector<ZoneShape*> allZones;                                          
std::vector<Zone<sf::CircleShape>*> estates = initializer->initVillageEstates();       
std::vector<Zone<sf::RectangleShape>*> communityAreas = initializer->initCommunityAreas();

It's not perfect (e.g. a Zone<SquareShape>* cannot necessarily stack with a Zone<RectangleShape>*), but it should allow you to have both generic containers for zones of all shape types as well as more specialized containers for specific shapes.

Upvotes: 1

Related Questions