I'd accept
a pointer to a base class and then call different functions based on its derived type.
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
#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 ?
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).