如何解析具有多个多态输入的方法调用

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

本文关键字:输入 方法 调用 多态 何解析      更新时间:2023-10-16

假设我们有这些重载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函数将被调用?

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

(我希望我的问题+例子足够清楚,但如果有歧义,请让我知道)

注:还有这个电话呢?

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

将调用哪个版本的油漆?

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

它的要点是,这段代码不会实现多态行为,因为它会为类似的代码在Java/c#。

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

如果你有:

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

编译器会报错,因为重载2和重载3都可以同样工作。

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

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

如果有歧义让我知道

是一个歧义;它在电话里!

paint(Circle{}, SolidColor{});

此调用是二义性的,因为对于此调用,没有重载比其他重载足够专门化。Clang给出这个错误:

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

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

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

仍有一个未解决的挑战!

除了已经很好的答案,在你的方法中有两个问题值得提及:

  • 要使用运行时多态性,您需要通过指针(如果可能的话智能指针)或引用传递参数。因为按值传递需要在编译时知道对象的大小。如果将子节点复制到父节点上,可能会导致切片。

  • 只有虚成员函数是多态的。非虚成员函数和非成员函数是在编译时根据声明的对象类型选择的。

如何解决?

要在非成员疼痛函数中引入运行时多态性,可以考虑使用模板方法设计模式:

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(); 
}

这要求您的非成员函数可以仅在预定义的框架的基础上表示,并且多态性只能在某些阶段依赖于单个对象的多态成员函数

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

是的,但是稍微复杂一点。最初的想法是使用多层多态性并结合重载来设计乒乓。这叫做双重调度。一般的思路是这样的:

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 
}