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) {}
显然,Shape
是 Circle
的父级,而 Color
是 SolidColor
的父级。
如果我像这样进行函数调用: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
}
假设我们有这些重载的 C++ 函数:
void paint(Shape s, Color c) {}
void paint(Circle ci, Color c) {}
void paint(Shape s, SolidColor sc) {}
显然,Shape
是 Circle
的父级,而 Color
是 SolidColor
的父级。
如果我像这样进行函数调用: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
}