views:

251

answers:

10

Hello, i have a problem with function overloading. I will show you with some simple example:

class A {};
class B : public A{};

void somefunction(A&, A&);
void somefunction(B&, B&);

void someotherfunction() {
...
A& a1 = ...
A& a2 = ...

...
}

Both a1 and a2 are instances of B but

somefunction(a1,a2);

calls

void somefunction(A&, A&);

What did i do wrong? I mean polymorphism and overloading are for stuff like that, arent they?

edit: Ok now i know it does not work (thanks for your answers).

Any solution how to do this? Without casting.

edit2: Ok left it as it is, with type casting, since something i would like to have is not possible. Thanks all for your help.

A: 

Yes but C++ decided which function to use at compile time, not at runtime. And at compile time the only thing the compiler sees is (A&, A&) - it cannot know that those are actually instances of B.

St3fan
Well but shouldnt the compiler use late binding for stuff like that?
George B.
'Late Binding' is something used for linking code. Not relevant for this case.
St3fan
@George: Nope. It *could* do this: examine the argument types at runtime, and determine which function to call. But that's not how the language is defined, so it won't. :)
jalf
Yes it could. I thought that it does (looks logical to me) but i guess there is a good reason not to do so.
George B.
UncleBens
A: 

You should post more code....what is
A& a1 = ...
A& a2 = ...

Shouldn't you use pointers? If you're storing a1 and a2 as type A then even if they are also B's the A overload gets called. You'd have to dynamic_cast them.

Chris H
I want to avoid casting..The code there isnt needed, its 100% sure they are B instances.
George B.
+8  A: 

Cast them statically so that the compiler knows which one to pick:

void somefunction((B&)a1, (B&)a2);

The reason why you are having this problem is with the program design, not the language. Compiler picks which which function is used based on the types that are passed in. C# will behave in exactly the same way (pretty sure Java will too).

It seems to me that you are implementing polymorphism in the wrong place. somefunction really belongs inside class a and should be virtual. Then whenever it's called on the instance of a at runtime the override in the right class will be called.

So, really it should be something like this:

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
  }
}

The above solution uses minimal casting inside the virtual function and assumes that the two instance of the same type. This means that b.somefunction(a()) will have undefined behaviour.

A better solution is to rely on C++ RTTI and use dynamic_cast, which will return NULL if the downcast is not possible.

This problem is known as double dispatch problem and is described in the wikipedia article pretty much as you described it. Furthermore, the only solution that wikipedia gives for multiple dispatch is to use dynamic_cast.

EDIT OK, this has been bugging me, here is the solution for full double dispatch between a base class and two subclasses. It aint pretty and uses a bit of C++ trickery like friend classes (for better encapsulation actually, rather than the reverse) and forward declarations.

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");
}
Igor Zevaka
George B.
Igor Zevaka
But i have to do some checks to see if its of type B or C to call the function. I want to avoid any checks or type casts.
George B.
Then you should use a more dynamic language and not c++
St3fan
+1 for double dispatch.
Daniel Earwicker
Wow, nice tricks you used there :)Well as i posted here (a few posts below), i used another solution (dispatch table), but because i really liked this post and it answers my question i consider this as the final answer to my problem. Thank you very much!
George B.
A: 

In this case compiler will always call somefunction(A&, A&);. Why would it call somefunction(B&, B&);?

fastcodejava
A: 

You said in a comment that you're SURE that they are B's.

If that is the case then this is what you want to do.

B a1(); B a2();

If you ever need A's, you can do this (A*)&B. That is an implicit cast and I'm pretty sure it happens at compile time.

Chris H
A: 

Your compiler has chosen what it thinks is the most appropriate overload. a1 and a2 are both declared as references to class A, so they fit the overload which takes references to class A "better" than they fit the other one, since that would require some sort of implicit cast to convert them to class B.

Note also that you can't implicitly upcast that way. If you have a pointer or reference to an instance of the base class (A in this case) then it can't be implicitly converted to a derived class, because in general not all instances of the base class are instances of the derived class (all Bs are As, but not all As are Bs).

You will need to declare them as instances of B before calling the function:

B& b1 = ...
B& b2 = ...
somefunction(b1, b2);
Peter
+2  A: 

As others have already mentioned, the compiler picks the correct overload - its how the language works.

If you are really sure of what type the instances are, you should just cast. If not, one way you can get around manual type-checking at run-time is 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);
Georg Fritzsche
+1  A: 

what exactly are you trying to do? it looks like you are trying to write a function that does something given two objects, and you want it to do a different thing based on the type of the combination of objects?

remember that even normal polymorphism does "checks" internally.

this is an interesting problem tho,

polymorphism gives you the ability to easily overload the functionality of a function based on the type of ONE object, not two.

what is it EXACTLY that you are trying to do? my best suggestion would be to make it so that each object could perform its own specific stuff separately and then return a common object for common processing:

class Base
{
  virtual SomeComonInterfaceObject DoMySpecialSomething() = 0;
}

void _doSomething(SomeComonInterfaceObject a, SomeComonInterfaceObject b);

void doSomething(Base& o1, Base& o2)
{ 
     _doSomething(o1->DoMySpecialSomething(), o2->DoMySpecialSomething());
}

if that doesn't suit, you probably just have to check the type and do specifics based on that.

note that even normal polymorphism does "checks" if you are worried about performance, any other language would have to too.

the only way you might be able to get around that is by using templates, and it would probably get real ugly.

would be interesting to know what you are trying to do. also, these doSomething functions, is it always the case that their two parameters are the same type? or do they mix and match?

matt
George B.
+3  A: 

The function to call is only determined at run-time for virtual methods, based on the type of the this object:

A* a = new B;
a->foo();  //calls B::foo (as long as foo is virtual)

The function to call is not resolved at run-time based on the "real" type of a function's arguments.

A* a = new B;
X* x = new Y;
a->foo(x); //assuming virtual and two overloads, calls B::foo(X*), not B::foo(Y*)

There is no built-in double dispatch mechanism (to select the function to call based on the dynamic types of two objects at the same time), although the pattern can be manually implemented as some posts show.

If you say that you always know that the A& will actually be B& and don't want casts, I conclude that the types will be hard-coded known at compile-time, so you might try "compile-time polymorphism" instead. (In this case A and B don't even need to be related, as long as they have a suitable interface.)

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

Now a1 and a2 will really be As in one instantiation and Bs in the other case, as far as selecting the overload is concerned. (I added some consts, because otherwise it would be harder to produce something that binds to non-const references.)

visitor
No the types wont be hard coded at compile time. But thanks for your try to help me!
George B.
I didn't mean strictly hard-coded, but generated at compile-time with templates. If the types were really only determined at run-time, I don't see how you can so confidently say that your casts will always be correct. - You know better, though, as it might mean very big architectural changes. Just keep in mind that inheritance is not a magic cure for all types of problems (I believe, problems like yours are partly the reason, why the STL was not implemented with runtime polymorphism).
visitor
A: 

What i will do is to use a dispatch table to get what i want. Instead of 1 dimensional it may be 2 or 3 dimensional (probably 2). Thanks all for trying to help me!

George B.