C++:如何解决具有多个多态输入的方法调用?

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

假设我们有这些重载的 C++ 函数:

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

显然,ShapeCircle 的父级,而 ColorSolidColor 的父级。

如果我像这样进行函数调用:paint(myCircle, mySolidColor) 将调用哪个版本的 paint 函数?

通常,当层次结构中有多个参数可以是任何类型时,如何解析具有多个解析候选者的方法调用?

(我希望我的问题+示例足够清楚,但如果有歧义请告诉我)

P.S。还有这个电话呢?

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

将调用哪个版本的画图?

if there is ambiguity let me know

有歧义;正在通话中!

paint(Circle{}, SolidColor{});

这个调用是不明确的,因为对于这个调用,没有比其他重载更专业的了。 Clang 给出此错误:

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?

这称为重载解析,这个主题过于庞大,无法在特定的 SO 答案中涵盖。 cppreference 关于该主题的文章应该为您提供合理的概述。

给定你的代码,元素将在传递给函数时被复制,并被切片:例如在第一次重载中,如果你传递 Shape s 将只是一个松散的 Circle 信息给它一个 Circle。 如果 Shape 是抽象的,则此代码甚至无法编译。

要点是这段代码不会像 Java / C# 中类似代码那样实现多态行为。

第二点是重载解析发生在运行时多态之前:也就是说,编译器在编译时通过选择最匹配的函数原型来选择调用哪个函数。

如果你有:

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

然后编译器将 complain due to ambiguity 因为重载 2 和 3 都可以同样工作。

最重要的是,对于 C++ 中的多态性,您希望通过引用传递参数:

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

还有一个未解决的挑战!

除了已经很好的答案之外,值得一提的是您的方法中的两个问题:

  • 要使用run-time 多态性,您需要通过指针(如果可能的话智能指针)或引用传递参数。因为按值传递需要在编译时知道 object 的大小。如果将 child 复制到 parent 上,可能会导致切片。

  • 只有虚成员函数是多态的。 Non-virtual 成员函数和非成员函数在compile-time 处根据object 声明的类型进行选择。

如何解决?使用模板方法 ?

要在非成员疼痛函数中引入 run-time 多态性,您可以考虑使用 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(); 
}

这要求你的非成员函数只能在预定义的骨架的基础上表达,并且多态性只能在某些阶段依赖于单个 objects[=14 的多态成员函数来实现=]

但是我可以做同时依赖于多个类型的多态吗?

是的,但稍微复杂一些。原始想法是使用多级多态性并结合重载来设计 ping-pong。它被称为 double dispatch。一般的想法是这样的:

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 
}