2

I'm wondering if it's possible to treat a class as its base type. I've made a small example below describing the situation i mean.
I use a Vector2 class that I've left out in the example, but it just consists of an x and y value of type float.
I'm trying to make the method called FindEntitiesWithPosition to work with the classes Person and Vechicle, but I get error E0312 no suitable user-defiend conversion from "std::vector<Person, std::allocator<Person>>" to "const std::vector<Entity, std::allocator<Entity>>" exists. How can I make this code work?

class Entity {
public:
    Entity (Vector2 position)
        : position(position) { }

    Vector2 position;
}

class Person : public Entity {
public:
    Person(Vector2 position, std::string name)
        : Entity(position), name(name) { }

    std::string name;
}

class Vehicle : public Entity {
public:
    Vehicle (Vector2 position, int power)
        : Entity(position), power(power) { }

    int power;
}

std::vector<Entity> FindEntitiesAtPosition(std::vector<Entity> entities, Vector2 position) {
    std::vector<Entity> result;
    for (Entity e : entities) {
        if (e.position == position)
            result.push_back(e);
    }
    return result;
}

int main() {
    std::vector<Person> persons;
    persons.emplace_back(Vector2(1.f, 1.f), "Alice");
    persons.emplace_back(Vector2(2.f, 2.f), "Bob");

    std::vector<Vehicle> vehicles;
    persons.emplace_back(Vector2(1.f, 1.f), 3);
    persons.emplace_back(Vector2(2.f, 2.f), 4);

    // Should return person with name "Bob"
    std::vector<Person> v1 = FindEntitiesAtPosition(persons, Vector2(2.f, 2.f));

    // Should return vehicle with power 4
    std::vector<Vehicle> v2 = FindEntitiesAtPosition(vehicles, Vector2(2.f, 2.f));
}
Guy Simonsen
  • 123
  • 5
  • I have no clue what you're asking about. Post a [mcve] as required here please. Maybe a helpful read: https://stackoverflow.com/questions/274626/what-is-object-slicing – πάντα ῥεῖ Jun 30 '19 at 12:41

2 Answers2

4

template matches your requirements.
I would suggest something like this.

template<typename T>
std::vector<T>
FindEntitiesAtPosition(const std::vector<T> &entities,
                       Vector2 position)
{
  std::vector<T> result;
  for(const auto &e: entities)
  {
    if(e.position == position)
    {
      result.emplace_back(e);
    }
  }
  return result;
}

By the way, watch out the unneeded copies when passing entities and in the range-for loop.

prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • Can it not be done without ```template``` by using the derived type ```Entity```? – Guy Simonsen Jun 30 '19 at 12:45
  • May be with something extremely convoluted, relying on dynamic polymorphism, à la Java... But I can't see any good reason (except nostalgia) to lean towards such a complicated solution. – prog-fh Jun 30 '19 at 12:52
  • @GuySimonsen prog-fh is correct. If you want to do this working by value (i.e. copying the real object), you need to use templates. Otherwise you'd risk object slicing. But using a by value semantic requires compile-time type resolution. So this won"t work if Vehicle would be derived further into Car and Plane. If you'd need run-time polymorphism, then you'll have to work with pointers (preferably shared pointers). – Christophe Jun 30 '19 at 13:12
4

You can treat an object of a derived class as an object of a base class. How? You can convert a pointer or a reference to an object of a derived class to a pointer or a reference to an object of a base class, and continue working with the converted pointer or reference. This conversion is implicit so it is a seamless transition from a (single) derived object to its base object. It also works with smart pointers.

Here's the point where things stop being nice. You cannot treat a vector (list, set, vector of vectors, bunch, whatever) of objects of a derived class as a vector (list, set, vector of vectors, bunch, whatever) of objects of a base class. The error message is telling you just that.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243