1

What is the neatest way of having a class like this,

class Object
{
public:
    virtual Object* Find(string name);
};

implemented so that its derived classes' Find() method automatically returns the derived class type without having to do something like this:

class DerivedObject : public Object
{
public:
    DerivedObject* Find(string name);
};

manually?

Extra: actually in my real implementation it's a bunch of static functions, not virtual ones. I've got functions like static Object* Object::Find(string name) and static GameObject* GameObject::Find(string name).

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
Kristian D'Amato
  • 3,996
  • 9
  • 45
  • 69
  • 1
    I believe you are out of luck – SwiftMango Oct 29 '13 at 23:22
  • Hmm... not even through templates, somehow? – Kristian D'Amato Oct 29 '13 at 23:29
  • The whole point of making `Find` virtual is so you can have a reference to an `Object` to call `Find` on. But that necessarily means you'll be expecting `Find` to return an `Object`, not a `DerivedObject`. – Adam Oct 29 '13 at 23:29
  • Well, I disagree, since it's part of the language that there's covariant types to make the above not an override but a virtual override. – Kristian D'Amato Oct 29 '13 at 23:37
  • The problem with doing that is that if you derive some object from the `Object` class, there's no guarantee that `DerivedObject::objects` is a vector containing only `DerivedObject` pointers. That vector could very easily still contain `Object` pointers, as its declared type is `vector` regardless – Silvio Mayolo Oct 30 '13 at 00:03
  • 1
    @KristianD'Amato Yes, it's legal to do what you're doing, but it only works if `DerivedObject::Find` is explicitly defined. The compiler won't guess. The closest you can get with templates is the CRTP, which will do what you ask, but `Object` becomes `Object` and that may or may not fit with the rest of your code. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – Adam Oct 30 '13 at 00:19
  • It should be possible to do something like this, but you'll probably have to get rid of the virtual function. And there are situations where you'll be forced to make some things `const`. – Aaron McDaid Oct 30 '13 at 00:20
  • Do you really need `Object` to contain a vector of `Object*`, and `DerivedObject` to contain a vector of `DerivedObject*`? Wouldn't it make more sense to have a `BaseContainer` that contains a vector of `Base*` and a `DerivedContainer` that contains a vector of `Derived*`? If so, please edit your question. – Aaron McDaid Oct 30 '13 at 00:23
  • @KristianD'Amato Take a look here: [Get derived type via base class virtual function](http://stackoverflow.com/questions/19501838/get-derived-type-via-base-class-virtual-function/19503045#19503045). I've tried to explain (and discussed) the problem in depth there. – πάντα ῥεῖ Oct 30 '13 at 00:23
  • 2
    What is your end goal here? – Shoe Oct 30 '13 at 00:40
  • @Aaron: Edited because the vector of objects was really superfluous. – Kristian D'Amato Oct 30 '13 at 00:40
  • @Jefffrey, I want to not have to type four variants of this `Find()` function (Find, FindAll, etc...) for every derived type that I create from `Object`. Something like the above would be very handy. – Kristian D'Amato Oct 30 '13 at 00:42
  • @KristianD'Amato, I still find it strange that each `Object` has a method that can return an `Object*`? Do you expect to be able to the following: do `Object object; object->Find("a")->Find("b")->Find("c")`? Do you want a recursive objects-containing-objects-containing-objects-... structure? Or perhaps this is a *Singleton* object where each `Object` has access to *all* `Objects`? So I ask again: maybe you want `ObjectContainer :: Find` returning `Object&`, and `DerivedContainer :: Find` return `Derived*`? – Aaron McDaid Oct 30 '13 at 00:43
  • @AaronMcDaid, actually in my real implementation it's a bunch of static functions, not virtual ones, so it would fit in better with what you've said. I've got functions like `static Object* Object::Find(string name)` and `static GameObject* GameObject::Find(string name)`. My first guess here was to use virtual functions for a more general sort of problem, which wouldn't work with static functions, so it's not precisely the same thing. – Kristian D'Amato Oct 30 '13 at 00:49
  • @Adam Thanks, the CRTP pattern looks more like what I had in mind. – Kristian D'Amato Oct 30 '13 at 00:51
  • @KristianD'Amato, I've copied that comment into the question. I think it's important. – Aaron McDaid Oct 30 '13 at 00:56

1 Answers1

2

Get rid of the virtual function, and write a free function instead.

template<typename T>
T* Find(T& object, string name) {
}

You'll have to call it via Find(derivedobject,name) instead of derivedobject.Find(name), but otherwise I think this will behave as you like.

You'll also have to declare this as a friend inside Object if you need it to have access to the protected data. Just put the following line inside the Object class.

template<typename T>
friend
T* Find(T& object, string);
Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88