Search code examples
c++overloadinghierarchyunique-ptr

Overloading function based on pointer type of unique_ptr parameter


I was under the impression that unique_ptr could infer class hierarchy in the same way that a normal pointer can, but when I try to overload a function like this:

void func(unique_ptr<Derived1>& d1);
void func(unique_ptr<Derived2>& d2);

And then call one of the functions like this:

unique_ptr<Base> b = make_unique<Derived1>();
func(b);    

I get an error saying that no instance of overloaded function "func" matches the argument list. The runtime type of b is Derived1, so I expected that the first overload would be called.

Also, when I return a unique_ptr from a function, the compiler is able to cast (? not sure of the appropriate terminology) a derived class to a base class, so that something like this works:

unique_ptr<Base> create(){
    return make_unique<Derived1>();
}

Often in my code I have variables declared as the result of functions like this. They're declared as base but have derived runtime types, and I would like to pass them into one single overloaded function.

How do I overload functions in the same way that I would with regular pointers where class hierarchies are involved?


Solution

  • You are right that the implicit conversion operations of all standard smart pointers model those of raw pointers. This allows the second snippet to compile, i.e.

    unique_ptr<Base> create(){
        return make_unique<Derived1>();
    }
    

    However, there is a misconception about the first snippet, as there is never a builtin implicit downcast-like conversion from base class to derived class. Using normal pointers,

    void func(Derived1* d1);
    void func(Derived2* d2);
    
    Base* b =  new Derived1();
    func(b);
    

    won't compile either. This makes sense in a the fundamental OOP sense - look at objects in an inheritance hierarchy through the base class interface, and hide the actual concrete runtime type.

    If you need a design with that kind of dispatch, you want to read about the "Visitor" design pattern, which implements a technique called double (or multiple) dispatch. But the amount of boilerplate necessary to realize that gives you a hint why the language doesn't provide this kind of dispatch as a builtin.