4.11. Overloading

In the previous section you might have noticed that fred and area perform similar functions—finding the area of a circle—but take different parameters. For area, we have to provide the radius; for fred, we provide two points.

If two functions do the same thing, it is natural to give them the same name. In other words, it would make more sense if fred were called area.

Having more than one function with the same name, which is called overloading, is legal in C++ as long as each version takes different parameters. So we can go ahead and rename fred:

double area (double xc, double yc, double xp, double yp) {
  return area (distance (xc, yc, xp, yp));
}

This looks like a recursive function, but it is not. Actually, this version of area is calling the other version. When you call an overloaded function, C++ knows which version you want by looking at the arguments that you provide. If you write:

double x = area (3.0);

C++ goes looking for a function named area that takes a double as an argument, and so it uses the first version. If you write

double x = area (1.0, 2.0, 4.0, 6.0);

C++ uses the second version of area.

Many of C++ standard library functions are overloaded, meaning that there are different versions that accept different numbers or types of parameters.

Function overloads are a huge advantage over C where (nearly) every function is global and every function name must be unique. For example:

  • C defines 7 different functions just for absolute value

    • abs, llabs, fabs, fabsf, etc. see abs

  • and 13 different functions for different types of see division operations.

This just adds to the amount of stuff programmers have to commit to memory. In C++, you only have to remember a single function to compute the absolute value: abs.

In order to count as a valid overload, either the number of parameters must be different, or the parameter types must be different, or a combination of both. For example:

addition

Example overloading-AC1
 1#include <iostream>
 2using std::cout;
 3
 4// add two ints
 5int add (int a, int b) {
 6  return a+b;
 7}
 8// add two doubles
 9double add (double a, double b) {
10  return a+b;
11}
12
13int main () {
14  int x=5, y=2;
15  double p=3.14, e=2.718;
16  cout << add (x,y) << '\n';
17  cout << add (p,e) << '\n';
18
19  // error: call to overloaded function add (double, int) ambiguous
20  // cout << add (31.4, 10) << '\n';
21
22  // explicit conversion is OK
23  cout << add (31.4, double(10)) << '\n';
24}

volume

Example volume-overloading-AC2
 1#include <iostream>
 2#include <cmath>
 3
 4namespace mesa {
 5   constexpr double pi = 3.141592653;
 6}
 7
 8// volume of a cube
 9double volume (const double a) {
10  return a * a * a;
11}
12
13// volume of a cylinder
14double volume (const double r, const double h) {
15  return mesa::pi * r * r * h;
16}
17
18// volume of a cuboid
19double volume (const double a, const double b, const double c) {
20  return a * b * c;
21}
22
23int main() {
24  std::cout << "volume of a 2 x 2 x 2 cube: "
25            << volume(2) << '\n'
26
27            << "volume of a cylinder, radius 2, height 3: "
28            << volume(2, 3) << '\n'
29
30            << "volume of a 2 x 3 x 4 cuboid: "
31            << volume(2, 3, 4) << '\n';
32  return 0;
33}

Note

The return type is not part of the overload.

Two functions in the same namespace that differ only in return type will not compile.

Overloading anti-patterns

How many parameters are too many?

This is an often asked question, with no clear cut answer. It is primarily a question of clarity and design.

For example, given:

int operate (float a, int b, long c, double d);

In this case, the parameters and function name provide no guidance on how to call this function. So four is probably too many parameters, simply because future usage errors are likely.

Keep in mind that more parameters equal more complexity. Limit the number of parameters you need in a given method. Also, be wary of overloads with the same number of parameters and different types. For example:

int operate (double a, int b);
int operate (int a, double b);

Depending on what operate does with it's parameters, reversing the order of the parameters could have drastic consequences. We just don't know without looking at the source code. In this case even two parameters is too many. It is almost certain someone will invoke the wrong version occasionally.

Warning

Although overloading is a useful feature, it should be used with caution. You might get yourself nicely confused if you are trying to debug one version of a function while accidentally calling a different one.

Actually, that reminds me of one of the cardinal rules of debugging: make sure that the version of the program you are looking at is the version of the program that is running! Some time you may find yourself making one change after another in your program, and seeing the same thing every time you run it. This is a warning sign that for one reason or another you are not running the version of the program you think you are. To check, stick in an output statement (it doesn't matter what it says) and make sure the behavior of the program changes accordingly.

Q1

Which of the following function declarations would be legal if it was added to the program below?

double price (int x, int y);
double price (double a, int b, string c);

Q2

What are the following functions an example of?

double price(int x, int y);
double price(double a, int b, string c);

Q3

Suppose you have written the following functions for baking cake. Is there anything wrong with how bake_cake was overloaded?

bool bake_cake (string cake_mix, int eggs, double milk, bool birthday);
bool bake_cake (string cake_mix, int eggs, double water, bool holiday);

Q4

Would adding both of the following function declarations to a program lead to an error?

int stock_price (string currency, int previous_price, double interest_rate );
double stock_price (string currency, int older_price, double interest_rate );


More to Explore