1

I'm having class, that contains few overloaded versions of one method. Every version takes one parameter - object - that object is always derived from one base class. (There is not method taking Base class parameter).

class Base { ... }
class Object1 : public Base { ... }
class Object2 : public Base { ... }
class Object3 : public Base { ... }

class T
{
    // ... 
    do_sth(const Object1& obj);
    do_sth(const Object2& obj);
    do_sth(const Object3& obj);
    // ...
}

Then I create vector with unique_ptr pointing to Base class, containing (only) derived class object.

std::vector<std::unique_ptr<Base>> vect;    // then some push_backs

Now I want to call T::do_sth for every object in vect, like this:

for (auto& object : vect)
    T_obj.do_sth(*object);

However, that way it's imposible, because render(*object) calls do_sth(Base), that doesn't even exist (and that wouldn't be wanted behaviour). I tried to replace that line with several different (using casting), but none of my attempts succeeded. How to fix that?

mdjdrn1
  • 51
  • 1
  • 8
  • Possible duplicate of [C++ cast to derived class](http://stackoverflow.com/questions/5313322/c-cast-to-derived-class) – François Andrieux Jan 06 '17 at 21:40
  • 2
    What are the function signatures for `do_sth` and `render`? Are you passing the objects by value? Can you provide a [Minimal, Complete, Verifiable Example](http://stackoverflow.com/help/mcve)? – qxz Jan 06 '17 at 21:42
  • @Quentin @qxz : I had a mistake in my example, i've edited that. `do_sth` parameter is always const reference. I tried `do_sth(dynamic_cast(*object));`, but that's nonsense... – mdjdrn1 Jan 06 '17 at 21:48
  • 1
    Did you consider virtual functions? – Quest Jan 06 '17 at 21:52
  • 3
    I think you might have a design issue here. Different functionality for derived classes should be implemented with a virtual function, not different external functions. When you write `T_obj.do_sth(*object);`, the specialization that gets called is determined at _compile time_, and therefore will treat the parameter like a `Base&` regardless of its runtime type. – qxz Jan 06 '17 at 21:52
  • @mdjdrn1 With the edited example, the problem is quite different: calling the correct overload, depending on the dynamic type of an object. See `dynamic_cast`, RTTI and the `Visitor Pattern` for different approaches. And of course, see if plain virtual dispatching does not already solve your problem if you make `do_sth` a virtual function of `Base`. – Quentin Jan 06 '17 at 21:59
  • @Quest: I considered virtual functions, but I'd like to avoid them, because they might make a lot of mess for me. – mdjdrn1 Jan 06 '17 at 22:03
  • 1
    @mdjdrn1 you may want to expand, in the question, on why virtual functions do not fit. There are a lot of ways in which that may be the case, but regarding code complexity and size they're the lightest. – Quentin Jan 06 '17 at 22:06
  • 1
    Yeah, seems like virtual functions are your best bet to _avoid_ a mess here. – qxz Jan 06 '17 at 22:16
  • @Quentin Actually there are too many "complex" - for me - reasons why virtual functions - I don't know how to expand that. I'm familiarized with Visitor Pattern, but as far as I know it requires use of virtual functions - that's the thing I want to avoid (or to do only if there will not be another possiblity). I tried to use `dynamic_cast`, but probably, I don't how to use it properly - and I didn't find any similar problem with solution. – mdjdrn1 Jan 06 '17 at 22:17
  • @qxz I think that the whole problem is in one line `T_obj.do_sth(*object);`, where `*object` needs to be casted properly (but I still don't know how). Adding virtual functions would require changes in every class derived from `Base` - that's also _messy_ (for me) – mdjdrn1 Jan 06 '17 at 22:21
  • 1
    What should happen for `Derived2` which is a base class of `Object2`, when there is `do_something(Object*)` and no `do_something(Derived2*)`? Should it match the version compatible with a base subobject, or do you want only exact matches? If you only want exact matches, you can dispatch based on `typeid`. If you need the usual inheritance overload rules, then Visitor (possibly with a CRTP helper class) is better. – Ben Voigt Jan 07 '17 at 19:25
  • @BenVoigt I want exact matches only. After Quentin's answer I actually considered "set of" conditional statements with `typeid` and I think that it looks even more clear than Quentin's version – mdjdrn1 Jan 08 '17 at 13:01

1 Answers1

4

Given the requirement "no modifying any Object class, no virtual functions" from the comments, your first solution here seems to be dynamic_cast, used thusly:

for (auto const& uObj : vect) {
    if(auto *obj = dynamic_cast<Object1 const *>(uObj.get()))
        T_obj.do_sth(*obj);
    else if(auto *obj = dynamic_cast<Object2 const *>(uObj.get()))
        T_obj.do_sth(*obj);
    else if( /* ... */)
        // And so on.
}

That's typically where I'd use a throwaway macro -- use at your own risk.

#define DYNAMIC_CALL_DO_STH(Type) \
    if(auto *obj = dynamic_cast<Type const *>(uObj.get())) \
        T_obj.do_sth(*obj)

for (auto const& uObj : vect) {
    DYNAMIC_CALL_DO_STH(Object1);
    else
    DYNAMIC_CALL_DO_STH(Object2);
    else
    DYNAMIC_CALL_DO_STH(Object3);
}

#undef DYNAMIC_CALL_DO_STH
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • If that's the only way to solve my problem without virtual methods, then now I see this solution is okay only for current structure of my project. Adding any new derived class entails modifications in that loop. If `do_sth` was virtual function, I'd be able to call it with range-based for loop.- much much simplier. Thanks for your answers. – mdjdrn1 Jan 06 '17 at 22:51
  • @mdjdrn1 well, you've pointed out all of the advantages and drawbacks of virtual functions ;) If you can be bothered to maintain a single list of derived classes in one place, some macro + template trickery can certainly be done to generate code, too. – Quentin Jan 06 '17 at 23:06