C++ enables several functions of the same name to be defined, as long as they have different signatures. This is called function overloading. The C++ compiler selects the proper function to call by examining the number, types and order of the arguments in the call. Function overloading is used to create several functions of the same name that perform similar tasks, but on different data types. For example, many functions in the math library are overloaded for different numeric types—the C++ standard requires float
, double
and long double
overloaded versions of the math library functions discussed in Section 6.3.
Overloading functions that perform closely related tasks can make programs more readable and understandable.
square
FunctionsFigure 6.20 uses overloaded square
functions to calculate the square of an int
(lines 7–10) and the square of a double
(lines 13–16). Line 19 invokes the int
version of function square
by passing the literal value 7
. C++ treats whole-number literal values as type int
. Similarly, line 21 invokes the double
version of function square
by passing the literal value 7.5
, which C++ treats as a double
. In each case the compiler chooses the proper function to call, based on the type of the argument. The last two lines of the output window confirm that the proper function was called in each case.
Overloaded functions are distinguished by their signatures. A signature is a combination of a function’s name and its parameter types (in order). The compiler encodes each function identifier with the types of its parameters (sometimes referred to as name mangling or name decoration) to enable type-safe linkage. Type-safe linkage ensures that the proper overloaded function is called and that the types of the arguments conform to the types of the parameters. Figure 6.21 was compiled with GNU C++. Rather than showing the execution output of the program (as we normally would), we show the mangled function names produced in assembly language by GNU C++.
For GNU C++, each mangled name (other than main
) begins with two underscores (__
) followed by the letter Z
, a number and the function name. The number that follows Z
specifies how many characters are in the function’s name. For example, function square
has 6 characters in its name, so its mangled name is prefixed with __Z6
. Following the function name is an encoding of its parameter list:
For function square
that receives an int
(line 5), i
represents int
, as shown in the output’s first line.
For function square
that receives a double
(line 10), d
represents double
, as shown in the output’s second line.
For function nothing1
(line 16), i
represents an int
, f
represents a float
, c
represents a char
and Ri
represents an int&
(i.e., a reference to an int
), as shown in the output’s third line.
For function nothing2
(line 20), c
represents a char
, i
represents an int
, Rf
represents a float&
and Rd
represents a double&
.
The compiler distinguishes the two square
functions by their parameter lists—one specifies i
for int
and the other d
for double
. The return types of the functions are not specified in the mangled names. Overloaded functions can have different return types, but if they do, they must also have different parameter lists. Function-name mangling is compiler specific. For example, Visual C++ produces the name square@@YAHH@Z
for the square function at line 5. The GNU C++ compiler did not mangle main
’s name; however, some compilers do. For example, Visual C++ uses _main
.
Creating overloaded functions with identical parameter lists and different return types is a compilation error.
The compiler uses only the parameter lists to distinguish between overloaded functions. Such functions need not have the same number of parameters. Use caution when overloading functions with default parameters, because this may cause ambiguity.
A function with default arguments omitted might be called identically to another overloaded function; this is a compilation error. For example, having a program that contains both a function that explicitly takes no arguments and a function of the same name that contains all default arguments results in a compilation error when an attempt is made to use that function name in a call passing no arguments. The compiler cannot determine which version of the function to choose.
In Chapter 10, we discuss how to overload operators to define how they should operate on objects of user-defined data types. (In fact, we’ve been using many overloaded operators to this point, including the stream insertion <<
and the stream extraction >>
operators, which are overloaded for all the fundamental types. We say more about overloading <<
and >>
to be able to handle objects of user-defined types in Chapter 10.)