0

I'd accept a pointer to a base class and then call different functions based on its derived type.

[ EDIT

Problem is: accept is a public method of a Manager class, which handles and stores lots of A, B1, B2 (shared_ptr of) instances. Manager will deal with them, based on runtime actual type

EDIT ]

#include <memory>
#include <iostream>

class A {};
class B1 : public A {};
class B2 : public A {};

void accept(std::shared_ptr<B1> sp)  { std::cout << "B1 "; }

// How to ensure the overload call to right derived type ?
void accept(std::shared_ptr<A> sp)   {
  std::cout << "A ";
  // if runtime type of sp.get is B1, do something
  // elif is B2, something else...
  // then if is A, do another thing
}

void accept(std::shared_ptr<B2> sp)  { std::cout << "B2 "; }

int main(int argc, char**argv)
{
    auto a = std::make_shared<A>();
    auto b1 = std::make_shared<B1>();
    auto b2 = std::make_shared<B2>();
    std::shared_ptr<A> ab2 = std::make_shared<B2>(): // !!!

    accept(a);
    accept(b1);
    accept(b2);
    accept(ab2);                                     // !!!
}

Output is A B1 B2 A. I want B2 for the latter.

Am I misunderstanding inheritance ?

Patrizio Bertoni
  • 2,582
  • 31
  • 43

1 Answers1

4

Without virtual dispatch, overloaded resolution is performed at compile-time, i.e. based on the static type of objects, not the dynamic type. Since ab2 is a std::shared_ptr<A>, the A overload is chosen.

To involve run-time type information (i.e. the dynamic type of the object), you must use virtual methods (and polymorphism), not overload resolution. It's not clear how to best incorporate this into the given example.

The most straightforward way is giving A a virtual destructor and some virtual method like accept() that is overridden in derived classes to dispatch to the most appropriate overload of the free function explicitly. There are other ways though, it depends on what you need.

It should also be noted that there is no SFINAE whatsoever in this code.


To reiterate and respond to the edit: If you want to make use of dynamic instead of static type information, your type must have at least one virtual method:

class A { virtual ~A(); };

If you do that, then you can use e.g. dynamic_cast (or, since you are using std::shared_ptr, std::dynamic_pointer_cast) to determine the run-time type of an object:

void accept(std::shared_ptr<B1> sp)  { std::cout << "B1 "; }

void accept(std::shared_ptr<B2> sp)  { std::cout << "B2 "; }

// How to ensure the overload call to right derived type ?
void accept(std::shared_ptr<A> sp) {
  if (std::shared_ptr<B1> ptrB1 = std::dynamic_pointer_cast<B1>(sp))
    accept(ptrB1);
  else if (std::shared_ptr<B2> ptrB2 = std::dynamic_pointer_cast<B2>(sp))
    accept(ptrB2);
  else
    // if is A, do another thing
}

But if you manually dynamic_cast between different types like this, then you probably don't have the right abstraction. Look into variants or the visitor pattern (or ask for this advice in a new question where you detail the problem and constraints you have).

Max Langhof
  • 23,383
  • 5
  • 39
  • 72
  • Problem is: `accept` is a public method of a _Manager_ class, which handles and stores lots of `A, B1, B2` (shared_ptr of) instances. Manager will deal with them, based on runtime actual type – Patrizio Bertoni Oct 14 '19 at 12:20
  • @PatrizioBertoni This is better discussed in a new question. You may also find useful resources in https://en.wikipedia.org/wiki/Double_dispatch or similar patterns. (Side note: There is no SFINAE here, if you think it is relevant then I believe you do misunderstand it). – Max Langhof Oct 14 '19 at 12:30
  • So there won't be any viable casting strategy? Either dynamic, reinterpret... – Patrizio Bertoni Oct 14 '19 at 12:47
  • There are many ways in which your problem could be solved, but we don't know which ones would make sense because we don't know your problem. I presented a straightforward way (polymorphism directly on the involved type hierarchy) but there are several other ways (all still using run-time type information through polymorphism, but on other classes, usually involving templates). I'm confident you will get useful recommendations if you ask a question about the actual problem you're trying to solve and the constraints you have, not just about the current solution attempt you are stuck with. – Max Langhof Oct 14 '19 at 12:51
  • Thanks, my struggle for MWE simplicity took me too far I guess. Just edited the question – Patrizio Bertoni Oct 14 '19 at 12:54
  • 1
    But no, casting alone won't overcome the limitations of compile-time (static) binding. You appear to need run-time type information, and the dynamic type of an object can only differ from its static type if you have `virtual` methods (i.e. polymorphism). – Max Langhof Oct 14 '19 at 12:55
  • @PatrizioBertoni For the record, substantially changing the question after answers have been added is considered inappropriate as it tends to invalidate existing answers. Your edit is somewhat borderline, and I've updated my answer in return, but please be wary of this in the future. – Max Langhof Oct 14 '19 at 13:04