我遇到了函数重载的问题。我将向您展示一些简单的例子:
class A {};
class B : public A{};
void somefunction(A&, A&);
void somefunction(B&, B&);
void someotherfunction() {
...
A& a1 = ...
A& a2 = ...
...
}
a1和a2都是B的实例,但
somefunction(a1,a2);
呼叫
void somefunction(A&, A&);
我做错了什么?我的意思是多态和重载就像那样的东西,不是吗?
编辑:好了,我知道它不起作用(感谢您的回答)。
任何解决方案如何做到这一点?没有铸造。
edit2: 好吧保持原样,使用类型转换,因为我想要的东西是不可能的。谢谢大家的帮助。
答案 0 :(得分:9)
静态抛出它们,以便编译器知道要选择哪一个:
void somefunction((B&)a1, (B&)a2);
您遇到此问题的原因是程序设计,而不是语言。编译器根据传入的类型选择使用哪个函数.C#的行为方式完全相同(非常确定Java也会这样)。
在我看来,你正在错误的地方实现多态。 somefunction
真正属于类a
,应该是虚拟的。然后,只要在运行时调用a
的实例,就会调用右类中的覆盖。
所以,真的应该是这样的:
class a {
public:
virtual somefunction(a& a2) {
//do stuff
}
}
class b : public a {
virtual somefunction(a& a2) {
b& b2 = (b&)a2;
//do stuff
}
}
class c : public b {
virtual somefunction(a& a2) {
c& c2 = (c&)a2;
//do stuff
}
}
上面的解决方案在虚函数中使用最小的强制转换,并假设两个实例的类型相同。这意味着b.somefunction(a())
将具有未定义的行为。
更好的解决方案是依赖C ++ RTTI并使用dynamic_cast,如果无法进行向下转换,则返回NULL。
这个问题被称为double dispatch problem,并且在维基百科文章中对其进行了描述。此外,维基百科为multiple dispatch提供的唯一解决方案是使用dynamic_cast
。
编辑好的,这一直困扰着我,这里是基类和两个子类之间完全双重调度的解决方案。它不是很漂亮,并且使用了一些像朋友类一样的C ++技巧(实际上是为了更好的封装,而不是反向)和前向声明。
class b;
class c;
class a {
protected:
virtual void somefunction(a& a2); //do stuff here
virtual void somefunction(b& b2); //delegate to b
virtual void somefunction(c& c2); //delegate to c
public:
virtual void doFunc(a& a2) {
a2.somefunction(*this);
}
friend class b;
friend class c;
};
class b : public a {
protected:
virtual void somefunction(a& a2); //do stuff here
virtual void somefunction(b& b2); //do stuff here
virtual void somefunction(c& c2); //delegate to c
public:
virtual void doFunc(a& a2) {
a2.somefunction(*this);
}
friend class a;
};
class c : public b {
protected:
virtual void somefunction(a& a2); //do stuff here
virtual void somefunction(b& b2); //do stuff here
virtual void somefunction(c& c2); //delegate to c
public:
virtual void doFunc(a& a2) {
a2.somefunction(*this);
}
friend class a;
friend class b;
};
//class a
void a::somefunction(a& a2) {
printf("Doing a<->a");
}
void a::somefunction(b& b2) {
b2.somefunction(*this);
}
void a::somefunction(c& c2) {
c2.somefunction(*this);
}
//class b
void b::somefunction(a& a2) {
printf("Doing b<->a");
}
void b::somefunction(b& b2) {
printf("Doing b<->b");
}
void b::somefunction(c& c2) {
c2.somefunction(*this);
}
//class c
void c::somefunction(a& a2) {
printf("Doing c<->a");
}
void c::somefunction(b& b2) {
printf("Doing c<->b");
}
void c::somefunction(c& c2) {
printf("Doing c<->c");
}
答案 1 :(得分:3)
要调用的函数仅在运行时针对虚拟方法确定,具体取决于此对象的类型:
A* a = new B;
a->foo(); //calls B::foo (as long as foo is virtual)
根据函数参数的“真实”类型,在运行时不会解析要调用的函数。
A* a = new B;
X* x = new Y;
a->foo(x); //assuming virtual and two overloads, calls B::foo(X*), not B::foo(Y*)
没有内置的双重调度机制(根据两个对象的动态类型同时选择要调用的函数),尽管可以像某些帖子一样手动实现模式。
如果你说总是知道那么A&amp;实际上将是B&amp;并且不希望强制转换,我得出结论,在编译时类型将是硬编码,因此您可以尝试“编译时多态”。 (在这种情况下,A和B甚至不需要相关,只要它们具有合适的接口。)
class A {};
class B {};
class C: public A {};
void somefunction(const A&, const A&);
void somefunction(const B&, const B&);
template <class T>
void someotherfunction()
{
const T& a1 = T();
const T& a2 = T();
somefunction(a1, a2);
}
int main()
{
someotherfunction<A>();
someotherfunction<B>();
//combine with inheritance and it will still be
//possible to call somefunction(A&, A&) since
//somefunction(C&, C&) is not defined
someotherfunction<C>();
}
现在a1和a2将确实在一个实例中为As,在另一种情况下为Bs,就选择过载而言。 (我添加了一些consts,因为否则会产生与非const引用绑定的东西更难。)
答案 2 :(得分:2)
正如其他人已经提到的那样,编译器会选择正确的重载 - 它是如何运行的。
如果您确定实例的类型,那么您应该进行投射。如果没有,可以在运行时绕过手动类型检查的一种方法是double dispatch:
struct A;
struct B;
struct Base {
virtual perform(Base& b) = 0;
virtual perform(A& a) = 0;
virtual perform(B& b) = 0;
};
struct A : Base {
virtual perform(Base& b) { b.perform(*this); }
virtual perform(A& a) { someFunction(a, *this); }
virtual perform(B& b) { someFunction(b, *this); }
};
struct B : A {
virtual perform(Base& b) { b.perform(*this); }
virtual perform(A& a) { someFunction(a, *this); }
virtual perform(B& b) { someFunction(b, *this); }
};
// ...
Base& b1 = foo1();
Base& b2 = foo2();
b1.perform(b2);
答案 3 :(得分:1)
请记住,即使是正常的多态也会在内部进行“检查”。
这是一个有趣的问题,
polymorphism使您能够根据ONE对象的类型轻松地重载函数的功能,而不是两个。
您正在尝试做什么?我最好的建议是让每个对象分别执行自己的特定内容,然后返回一个共同的对象进行常规处理:
class Base
{
virtual SomeComonInterfaceObject DoMySpecialSomething() = 0;
}
void _doSomething(SomeComonInterfaceObject a, SomeComonInterfaceObject b);
void doSomething(Base& o1, Base& o2)
{
_doSomething(o1->DoMySpecialSomething(), o2->DoMySpecialSomething());
}
如果不合适,您可能只需检查类型并根据该类型执行具体操作。
请注意,即使是普通的多态也会“检查”你是否担心性能,其他语言也必须这样做。
你能够解决的唯一方法是使用模板,它可能会变得非常丑陋。
知道你想要做什么会很有趣。另外,这些doSomething函数,总是这样,它们的两个参数是同一类型吗?或者他们混合搭配?
答案 4 :(得分:0)
是的,但是C ++决定在编译时使用哪个函数,而不是在运行时。在编译时,编译器唯一看到的是(A&amp;,A&amp;) - 它无法知道那些实际上是B的实例。
答案 5 :(得分:0)
你应该发布更多代码....什么是
A和a1 = ...
A和a2 = ......
你不应该使用指针吗? 如果你将a1和a2存储为A类,那么即使它们也是B,也会调用A重载。你必须动态播放它们。
答案 6 :(得分:0)
在这种情况下,编译器将始终调用somefunction(A&, A&);
。为什么会拨打somefunction(B&, B&);
?
答案 7 :(得分:0)
你在评论中说你确定他们是B的。
如果是这种情况,那么这就是你想要做的。
B a1(); B a2();
如果你需要A,你可以这样做(A *)和B。这是一个隐式演员,我很确定它会在编译时发生。
答案 8 :(得分:0)
您的编译器选择了它认为最合适的重载。 a1和a2都被声明为对A类的引用,因此它们适合重载,它引用A类“更好”而不是它们适合另一个,因为这需要某种隐式转换来将它们转换为B类。 / p>
另请注意,您不能以这种方式隐式转播。如果您有一个指针或对基类实例的引用(在本例中为A),则它不能隐式转换为派生类,因为通常并非基类的所有实例都是派生类的实例(所有的B都是As,但不是所有的B都是。)
在调用函数之前,您需要将它们声明为B的实例:
B& b1 = ...
B& b2 = ...
somefunction(b1, b2);
答案 9 :(得分:0)
我要做的是使用调度表来获得我想要的东西。它可以是2维或3维(可能是2维),而不是1维。谢谢所有人试图帮助我!