Function Overloading

- In C++, multiple function definitions can be written for the same function name

- As we’ve seen with constructor functions, this concept is referred to as function overloading

- Overloaded functions must have...
        a) a different number of arguments or,
        b) different types of arguments or,
        c) both
- Overloaded functions can be...
        a) Standard functions (not as members of a class)
        b) Class member functions (methods and constructors)
        c) Operator functions (as we’ll see later)
- Overloading allows function calls to the same name to respond differently depending on the parameter types

- Overloading, as we’ll see later, plays a key role in OO features such as inheritance and polymorphism

- Consider the following example with overloaded standard functions

 

#include <iostream>

using namespace std;

 

int add( int, int );

int add( int * );

 

main()

{

  int a = 10;

  int b = 20;

  int f1 = add( a, b );

  cout << f1 << endl;

 

  int c[2] = { 10, 20 };

  int f2 = add( c );

  cout << f2 << endl;

}

 

int add( int a, int b )

{

  return (a+b);

}

 

int add( int *c )

{

  return (c[0] + c[1]);

}

 

Output

30

30

 

- The same function to add two numbers is provided in two different forms

- The first function adds integers sent as separate variables while the second adds integers stored together in an array

- Depending on how a program stores information, the function best suited can be called

- Consider a second example with overloaded class member functions (methods)

 

#include <iostream>

using namespace std;

class Point
{
private:
    int x,y;

public:
    Point( int xo=0, int yo=0 ) { x=xo; y=yo; }
    void Print()
    {
        cout << "X, Y: “ << x << “, “ << y << endl;
    }
    void setXY( int xp, int yp )
    {
        x = xp;
        y = yp;
    }
    void setXY( Point &p )
    {
        x = p.getX();
        y = p.getY();
    }
    int getX() { return x; }
    int getY() { return y; }
};

 

- The method setXY is overloaded with two definitions and different parameter lists

- The first method accepts two integers while the second accepts a reference to a Point object

- Overloading allows flexibility depending on how information is passed in the input parameter list

- Below, p calls the first setXY method (with 2 integers) while q calls the second (with a Point reference)

main()
{
    Point p, q;

    p.setXY( 10, 20 );
    q.setXY( p );
    p.Print();
    q.Print();
}

Output

X, Y: 10 20

X, Y: 10 20

 

 

 

 

Overloading ambiguities

- Care must be taken when designing overloaded functions to avoid ambiguities (confusion)

- During processing, the compiler must determine which overloaded function to use
- In some cases, ambiguities can arise when making this determination
- Ambiguity can arise when overloaded functions differ only by their return type

- It is illegal for two or more functions to differ only by return type

- The compiler, in these cases, cannot determine which function to call (ambiguous)

- Consider the example and resultant compiler error below

 

#include <iostream>

using namespace std;

 

// Error, functions differ by return type only

int add( int, int );

float add( int, int );

 

main()

{

  int a = 10;

  int b = 20;

  int f1 = add( a, b );

  cout << f1 << endl;

 

  float f2 = add( a, b );

  cout << f2 << endl;

}

 

int add( int a, int b )

{

  return (a+b);

}

 

int add( int a, int b )

{

  return float( a + b );

}

 

Compilation Error

error: new declaration `float add(int, int)'

error: ambiguates old declaration `int add(int, int)'

In function `int main()':

 

 

- Other cases of ambiguities can arise when using default values for arguments in the parameter lists

- Care must be taken to assure that parameter lists are distinguishable by the compiler

- Consider the following example

 

#include <iostream>

using namespace std;

 

int add( int a=10, int=20 );

int add( int a=10 );

 

main()

{

  int f1 = add();

  cout << f1 << endl;

}

 

int add( int a, int b )

{

  return (a+b);

}

 

int add( int a )

{

  return (a + 2);

}

 

 

- Are these two functions really different? Which one will the compiler call in the main program?

- Because of the default values, the compiler cannot determine which one to call in the program (ambiguous)

- In fact, this program will not compile and reports the following errors

 

In function `int main()':

error: call of overloaded `add()' is ambiguous

error: candidates are: int add(int, int)

error:                 int add(int)

 

 

- Overloaded functions with default values must be unique in their default argument list

- We can correct the ambiguity by removing the default values to our arguments

 

#include <iostream>

using namespace std;

 

int add( int, int );

int add( int );

 

main()

{

  int f1 = add( 10, 20 );

  cout << f1 << endl;

 

  int f2 = add( 30 );

  cout << f2 << endl;

}

 

int add( int a, int b )

{

  return (a+b);

}

 

int add( int a )

{

  return (a + 2);

}

 

Output

30

32

 

 

- Similarly, care must be taken to avoid ambiguity in overloaded constructors with default argument values

- In the example below, the compiler cannot determine which of the first two constructors to call in the program

- As a result, an ambiguity occurs and the compiler reports an error

 

#include <iostream>

using namespace std;

 

class Point

{

private:

  float x,y,z;

 

public:

  Point()

  {

    x = y = z = 0.0;

  }

  Point(float c1=0.0, float c2=0.0, float c3=0.0)

  {

    x = c1;

    y = c2;

    z = c3;

  }

  Point(float *c)

  {

    x = c[0];

    y = c[1];

    z = c[2];

  }

  void Print()

  {

    cout << "x y z: “ << x << “ “ << y << “ “ << z << endl;

  }

};

 

main()

{

  Point p1;

  p1.Print();

}

 

Compilation Error

p.C: In function `int main()':

p.C:34: error: call of overloaded `Point()' is ambiguous

p.C:15: error: candidates are: Point::Point(float, float, float)

p.C:11: error:                 Point::Point()

 

 

- Even when all requirements are met, overloaded functions can still cause compiler ambiguities

- This is often the case when the same argument value can be used for different variable types
- Consider the example below, which overloaded function should the compiler select in the program?

 

#include <iostream>

using namespace std;

void myfunc( float f )
{
    cout << “myfunc( float ) called” << endl;
}
void myfunc( char *p )
{
    cout << “myfunc( char * ) called” << endl;
}

main()
{
    myfunc( 0 );
}

 

- Since the value zero can be stored in a float as well as a char pointer, the compiler cannot resolve the choice

- In this case, an ambiguity results and the compiler reports the following error

 

In function `int main()':

error: call of overloaded `myfunc(int)' is ambiguous

error: candidates are: void myfunc(float)

error:                 void myfunc(char*)

 

 

- Ambiguous functions in one example, however, can be non-ambiguous in another

- Using the same overloaded functions above, the following program compiles correctly

- The compiler can correctly distinguish between an acceptable floating point value and a char pointer

main()
{
    myfunc( 3.14 );
    myfunc( "A string" );
}

Output

myfunc( float ) called

myfunc( char * ) called


- Explicit casts in function calls can also help make the choice distinguishable to the compiler

main()

{

  myfunc( float(0) );         // C++ style of casting

  myfunc( (char *)0 );        // C style of casting

}

 

Output

myfunc( float ) called

myfunc( char * ) called