Function overloading

Hi, I've been working with a tree-like structure and encountered the following issue that I'll try to formulate in a simplified setting. Consider the following 2 classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class B;
//--------------------------
class A
{
public:
 void F(A& a){
  F_(a); // Want to call either F_(A&) or F_(B&)
 };
 virtual void F_(A&)=0; 
 virtual void F_(B&)=0;
};
//-------------------------------
class B : public A
{
public:
 virtual void F_(A& a){
  std::cout << "a" << std::endl;
 };
 virtual void F_(B& b){
  std::cout << "b" << std::endl;
 };
};
//--------------------------------------
int main(array<System::String ^> ^args)
{
 B b1, b2;
 A &b=b1;
 b.F(b2);   // I want b.F to call  F_(B&) !!! 
 return 0;
}


I thought that b.F(b2) would eventually result in calling F_(B&), however when I run the program in VC++ 2005, it outputs "a". That indicates that no overloading takes place. So what do I do wrong?

If I replace b.F(b2) with b.F_(b2), then everything works as expected and the program outputs "b". I don't like this solution though, as I was hoping to keep the interface of A clean by declaring the functions F_ private.

And finally, in the code above, the child class B appears in the interface of the parent class A: virtual void F_(B&)=0. Is there any way to avoid that and yet achieve the desired function overloading?

Thanks,
Eugene
Last edited on
just saw your explanation

I do not understand why are you calling b.F(b2); and not b.F_(b2);? The second one does print out b, as expected.

When c++ calls b.F(b2) it "typecasts" (or "downgrades" if you wish) your b2 object to object of type A. Then naturally it has no choice but to call F_(A&).

My comment is not an answer, just tried to identify in more detail where the problem lies.
Last edited on
closed account (z05DSL3A)
I'm not entirely sure what your requirements are, but is it somthing like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>

//--------------------------
class A
{
public:
    void F()
    {
        F_();
    };
private:
    virtual void F_()=0; 
};
//-------------------------------
class B : public A
{
private:
    virtual void F_()
    {
        std::cout << "b" << std::endl;
    };

};
//-------------------------------
class C : public A
{
private:
    virtual void F_()
    {
        std::cout << "c" << std::endl;
    };
};
//--------------------------------------
int main()
{
    B b1;
    C c1;

    A& a1 = b1;
    A& a2 = c1;

    a1.F();
    a2.F();

    return 0;
}


Or maybe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

//--------------------------
class A
{
public:
    static void F(A& a)
    {
        a.F_();
    };
private:
    virtual void F_()=0; 
};
//-------------------------------
class B : public A
{
private:
    virtual void F_()
    {
        std::cout << "b" << std::endl;
    };

};
//-------------------------------
class C : public A
{
private:
    virtual void F_()
    {
        std::cout << "c" << std::endl;
    };
};
//--------------------------------------
int main()
{
    B b1;
    C c1;
    
    A::F(b1);
    A::F(c1);
    
    return 0;
}
Last edited on
One option is to use dynamic_cast to determine the type at runtime:

1
2
3
4
5
6
7
8
9
10
11
12
13
void F(A& a)
{
  try
  {
    B& b = dynamic_cast<B&>(a);
    F_(b);        // dynamic cast successful, call B version
  }
  catch(...)
  {
    F_(a);        // dynamic cast failed, call A version 
  }
};
//.. 


Or if you want to avoid the try/catch reference version of dynamic_cast, you can use the pointer version:

1
2
3
4
5
6
void F(A& a)
{
  B* b = dynamic_cast<B*>(&a);
  if(b)    F_(*b);
  else     F_(a);
}


Or if you want to avoid dynamic cast completely (since it can be computational expensive), you can build in your own kind of simplistic runtime check for this kind of thing. This would be easier if you have several of these kinds of classes and not just 2 (trying to dynamic_cast to each of them would be a killer):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class B : public class A
{
public:
  virtual int getType() { return type_b; }

//...

class A
{
public:
  virtual int getType() { return type_a; }

  void F(A& a)
  {
    switch(a.getType())
    {
    case type_a:  F_(a);                   break;
    case type_b:  F_(static_cast<B&>(a));  break;
    }
  }
};
Last edited on
Thank you folks for all the replies, I've learned a lot from them.
However, I am still not sure how to implement the polymorphism based on the function argument type while keeping the parent class A to be completely unaware of its children classes.
Ideally, I'd like to have something like
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class A
{
public:
 void F(A& a){
    F_(a); // Want to call 
 };
private:
 virtual void F_(A&)=0; 
};
//-----------------------------------------------
void A::F_(A& a){
   std::cout << "Syntax Error" << std::endl;
};
//----------------------------------------------
class B : public A
{
  virtual void F_(B& b){
     std::cout << "bb" << std::endl;
 };
};
//----------------------------------------------
class C : public A
{
  virtual void F_(C& c){
     std::cout << "cc" << std::endl;
 };
  virtual void F_(b& b){
     std::cout << "cb" << std::endl;
 };
};
//--------------------------------------
int main(array<System::String ^> ^args)
{
 B b1, b2;
 A &b=b1;
 b.F(b2);   // want to call  B::F_(B&) and output "bb"

 C c1, c2;
 b.F(c2)     // want to call A::F_(A&) and output "Syntax Error"

 A& c=c1; 
 c.F(c2);    // want to call C::F_(C&)  and output "cc"
 c.F(b2)     // want to call C::F_(B&)  and output "cb"
 return 0;
}


Unfortunately, this would always invoke A::F_(A&).
It looks like what you want to do is not directly possible in C++. I think you have a fundamental misunderstanding of polymorphism and/or inheritance.

Polymorphism does not translate from one function type to another function type. For example void F_(A&); and void F_(B&); are two completely different function types despite them having the same name and parameter count. C::F_(B&) does not polymorph A::F_(A&) because they're two different functions.

The only way to have a function in class A call a function in a child class without A being aware of the children classes is for A to share a virtual function with the child.

ie:

1
2
3
4
5
6
7
8
9
10
class C : public A
{
  virtual void F_(B& b);  // the only way for A to call this function...
};

class A
{
  virtual void F_(B& b);  // is for it to have that same function
  virtual void F_(A& a);  // this does nothing at all to help you here
};


Of course you could say this requires A to be aware of children classes. Which brings me back to my original point: not directly possible in C++.

The best you can do is have children classes polymorph the function that "forks" the type (in this example, that would be A::F(A&)). This would require runtime checks of the type (so either dynamic_cast or a method similar to what I talked about in my previous post) to figure out which function to fork to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A
{
virtual void F(A& a);
};

class C : public class A
{
virtual void F(A& a)
{
  switch(a.getType())
  {
    case type_a:  F_(a);                   break;
    case type_b:  F_(static_cast<B&>(a));  break;
    case type_c:  F_(static_cast<C&>(a));  break;
    //.. etc
  }
}
};

Thank you Disch, that clarifies a lot of things for me. I am trying to adapt to C++ as I was a bit spoiled by programming in typeless languages in the last few years (although I haven't used function overloading either).

Using downcasting is very tempting indeed, even though this can become a maintenance issue later on. I guess, I'd have to completely redesign the code in order to get rid of the downcast altogether.
Thanks again,
Eugene
Why can't you turn the problem around?

1
2
3
4
5
6
7
8
9
class A
{
public:
 void F(A& a){
    a.F_();   // Instead of F_( a )?
 };
private:
 virtual void F_()=0;   // No argument
};

Topic archived. No new replies allowed.