C++: How method calls with multiple polymorphic inputs are resolved?


Keywords:c++ 


Question: 

Suppose that we have these overloaded C++ functions:

void paint(Shape s, Color c) {}
void paint(Circle ci, Color c) {}
void paint(Shape s, SolidColor sc) {}

Where obviously, Shape is parent of Circle and Color is the parent of SolidColor.

If I make a function call like this: paint(myCircle, mySolidColor) which version of paint function will be called?

Generally, how a method call with multiple resolution candidates is resolved when there is more than one parameter which can be of any type in a hierarchy?

(I hope my question + example is clear enough, but if there is ambiguity let me know)

P.S. Also what about this call?

Color* c = create_color();  //returns SolidColor instance
Shape* s = create_shape();  //returns Circle instance
paint(s,c);

Which version of paint will be called?


3 Answers: 

Given your code, the elements will be copied when passed to the function, and sliced : for instance in the first overload, Shape s will only be a loose the Circle information if you pass a Circle to it. If Shape is abstract this code won't even compile.

The gist of it is that this code won't achieve polymorphic behavior as it would for similar-looking code in Java / C#.

The second point is that overload resolution takes place before runtime polymorphism : that is, the compiler choses at compile time which function to call, by choosing the most matching function prototype.

If you have :

int main() { 
  Circle myCircle;
  SolidColor mySolidColor;
  paint(myCircle, mySolidColor);
}

Then the compiler will complain due to ambiguity since overload 2 and 3 could both equally work.

Most of all, for polymorphism in C++ you want to pass your arguments by reference instead :

void paint(Shape& s, Color& c) {}
void paint(Circle& ci, Color& c) {}
void paint(Shape& s, SolidColor& sc) {}


if there is ambiguity let me know

There is an ambiguity; it's in the call!

paint(Circle{}, SolidColor{});

This call is ambiguous, because no overload is sufficiently more specialized than the others for this call. Clang gives this error:

main.cpp:11:5: error: call to 'paint' is ambiguous
    paint(Circle{}, SolidColor{});

Generally, how a method call with multiple resolution candidates is resolved when there is more than one parameter which can be of any type in a hierarchy?

This is called overload resolution, and is far too vast a topic to cover in a specific SO answer. the cppreference article on the topic should provide a reasonable overview for you.



There is still an unsolved challenge !

In addition to the already excellent answers, it's worth to mention two issues in your approach:

  • To use run-time polymorphism you need to pass arguments by pointer (if possible smart pointer) or by reference. Because passing by value requires the size of the object be known at compile time. And it might cause slicing if copying a child onto a parent.

  • Only virtual member functions are polymorphic. Non-virtual member functions and non member functions are chosen at compile-time on the base of the declared type of the objects.

How to solve it ? With a template method ?

To introduce run-time polymorphism in you non member pain function, you could consider to use the template method design pattern:

class Shape {
public: 
    virtual void paint_shape() = 0 ; 
    virtual ~Shape() {}     // polymorphic class => better have destructor being virtual 
};    

class Color {
public: 
    virtual void set_color() = 0;
    virtual void reset_color() = 0;
    virtual ~Color() {}  
};    
...
void paint(Shape *s, Color *c) {
     activate_window (); 
     c->set_color();     // use polymorphic function 
     s->paint_shape(); 
     c->set_color();     // use polymorphic function 
     refresh_window(); 
}

This requires that your non member function could be expressed solely on the base of a predefined skeletton, and that polymorphism could be achieved only relying at some stages to polymorphic member functions of single objects

But can I do polymorphism that depends simultaneously on several type ?

Yes, but it's slightly more complex. The raw idea would be to design a ping-pong using multiple levels of polymorphism, combined with overloading. It's called the double dispatch. The general ideas is something like:

class Color; 
class Shape {
public: 
    virtual void paint_color(Color *c) = 0 ; 
    virtual ~Shape() {} 
};    
class Circle : public Shape {
public:
    void paint_color(Color *c) override;  
};

class Color {
public: 
    virtual void paint_shape (Circle*) = 0;
    virtual void paint_shape (Square*) = 0;
    ...
    virtual ~Color() {}  
};    
class SolidColor : public Color {
public: 
    void paint_shape (Circle*) override ;
    void paint_shape (Square*) override ;
};    

void Circle::paint_color(Color *c) {
    c->paint_shape (this);  // this is Circle*  => bounce on polymorphic color
}                           //                     with real determined shape    
void SolidColor::paint_shape(Circle *c) {
    paint_shape_and_color (c, this);  // this is SolidColor* => bounce on overload with 
}                           //                     determined shape and color 


void paint(Shape *s, Color *c) {
     s->paint_shape(c);   // polymorphic call 
}
void paint_shape_and_color (Circle *s, SolidColor *c) {
     ...  // real painting 
}