UPI Study › Courses › C++ Programming › Free lessons › Lesson 8.6 Free with a free account
Function Overloading in Action
C++ Programming · Chapter 8: FunctionsSection 8.6 · lesson 56 of 84video coming soon
← How Arguments TravelNext lesson: Recursive Calls →
In this lesson
One function name, multiple behaviors.
- State what makes two functions distinct overloads
- Explain why return type can't distinguish them
- Follow how the compiler resolves a call
- Diagnose and fix ambiguity; choose overloads or defaults
Transcript
Everything said in the video, section by section (1,438 words). Read along, or search it.
Opening Question
One Name, Several Functions
C requires every function to have a unique name, which is why its library offers abs, labs, and fabs, for one idea. C++ lets several functions share a name, so long as the compiler can tell the calls apart. This lesson covers exactly what tell apart means, how the choice is made, and what happens when it can't be made at all.
Definition
Signatures Make Overloads Distinct
Two functions may share a name, provided their signatures differ. A signature is the function's name together with the types of its parameters: their number, their types, and the order they appear in. So print can take an int, a double, a string, or an int and a width. The call site then reads naturally. Print 42, print 3.14, print total, and print 42 with 8, all say the same thing about intent. Run it, and the compiler sorts out which function is meant.
Common Mistake
What Counts as a Real Difference
Several things that look like differences aren't. Different parameter types make distinct overloads, and so does a different number of parameters. Same types in a different order also count, because order is part of the signature. But two declarations differing only in parameter names declare the same function twice. Names aren't part of the signature. And const on a by-value parameter changes nothing for the caller, since the function works on its own copy either way. On a reference, though, const is part of the type being referred to, and it does matter to the caller. So an int reference and a const int reference are distinct.
Key Rule
Why Return Type Cannot Distinguish
It seems as though the return type should distinguish overloads. The reason it can't is a matter of what's available at the point of the call. Declaring measure twice, once returning int and once returning double, is an error. The compiler says so. The compiler must choose a function using the call itself, and a call carries arguments. The return value may be assigned, converted, or ignored entirely, as in a bare call to measure. That statement would have no answer. So C++ takes the simpler road. The choice is made from the arguments, and from nothing else.
How It Works
Three Stages of Overload Resolution
Given a call, the compiler works through three stages. Knowing them turns confusing error messages into readable ones. First, the candidates: every visible function with that name. Second, the viable ones: the right number of arguments, each convertible to its parameter. If none are viable, the error is no matching function. Third, the best one, ranked by how little conversion each requires. If two are equally good, the error is ambiguous. Both failures happen while compiling. The ranking, best first: an exact match. Then a promotion, a widening the language treats as natural, such as char to int, or float to double. Then a standard conversion, anything else built in, such as int to double, or anything to bool. And last, a conversion your own types supply. Less work always beats more.
Worked Example
Ranking Calls to Show
Apply the rules to three overloads of show: A takes an int, B a double, and C a char. Show 42 picks A, an exact match. Show 3.5 picks B. And show x, in single quotes, picks C. A float literal picks B, because float promotes to double. And show true picks A, because bool promotes to int. But show 7L, a long, is ambiguous. A long reaches every one of them by a rank-three conversion, so no candidate is better than the rest. The compiler refuses. Ambiguity is an error, never a quiet guess.
Variation
Removing the Char Overload
Take C away and see. With only the int and double versions, show x picks A, the int. Char to int is a promotion, and promotion beats conversion. The program prints A.
Error Analysis
When Two Candidates Tie
An ambiguity error doesn't mean the compiler is being unhelpful. It means two candidates are genuinely equally good, and it won't guess which you meant. Weigh is overloaded for long and for double. Weigh 5 is ambiguous, because int to long and int to double are both rank-three conversions. The error lists both candidates. There are three usual causes. Two candidates each needing a standard conversion of the same rank, as here. Two candidates each matching one argument exactly and converting the other, so neither is better on both. And a default argument making a two-parameter version callable with one argument, so both fit a one-argument call exactly. Try that one: h of 5 is ambiguous too. Equally good means the compiler will not choose for you.
Fixes
Make the Call Exact
Four fixes are available, roughly in order of preference. Make the call exact. Weigh 5.0, or weigh 5L, states which overload is meant, right where the question arises.
More Fixes
Add, Remove, or Rename Overloads
Add the missing overload. If weighing an int is sensible, providing it removes the ambiguity for every such call at once. Remove an overload. Often the two were never meaningfully different. Or give them different names: weigh grams and weigh kilos, if that's what's really going on. When you read the error, the list of candidates is the useful part. Look at what each requires of your argument, and you'll find two conversions of equal rank. No matching function is the opposite problem: nothing was viable at all.
Alternative
Default Arguments Versus Overloading
A default argument gives a parameter a value to use when the caller omits it, so one function can be called several ways. Report can be called with just a title, using a width of 40 and a dot fill. Or with a width of 6, or with a dash fill too. Defaults must be the trailing parameters, and they're stated once, in the declaration. With default arguments, you write one function with one body. That's best when the logic is identical, and some inputs have a sensible fallback. Its risk is a long tail of optional parameters that's hard to read at the call. With overloading, you write several bodies. That's best when behavior genuinely differs by type, or by how many things are supplied. Its risk is ambiguity between convertible types.
Decision Rule
Same Operation or Different Operations
The deciding question is whether the versions do the same thing. Printing with a default width is one operation with a fallback, so a default argument is right. Printing an int and printing a string are two operations that happen to share a name, so overloading is right. And combining the two on the same name invites exactly the ambiguity from the h of 5 example. So do it deliberately, or not at all.
Big Picture
Overloading as Compile-Time Polymorphism
Polymorphism means one name standing for several behaviors. Overloading is one form of it, and the defining feature is when the behavior is chosen. The compiler chooses, while compiling, before the program has ever run, using the written types of the arguments. There's no cost at run time, and the choice can't change while the program runs. That's why the qualifier compile-time is needed. There's a second kind of polymorphism, in which the choice depends on what a value turns out to be while the program is running. It's a different mechanism, with a different cost. Keep the two apart: one is decided by the compiler from written types, the other later, from actual values. A later chapter builds on exactly that.
Guidelines
Using Overloading Well
A few guidelines for using overloading well. Overload only for the same idea. A shared name is a promise that the operations mean the same thing. Draw circle and draw square keep the promise. Process order and process refund may not. Keep behavior consistent: overloads that differ in what they return or in side effects surprise readers, because the call site looks identical. Avoid overloading on freely convertible types. Int, long and double convert into one another readily, which is exactly what produces ambiguity. And prefer clear names over clever ones. If you need a comment to say which overload is which, distinct names were the better design.
Quick Check
Predicting Calls to Tag
Quick check, with a new pair. Tag takes a long, or a double. There's no int version, and no char version. Which one does tag of 2.5f call? And what happens with tag of x, in single quotes? Pause and predict. Tag of 2.5f calls the double version, because float promotes to double. Tag of x doesn't compile. A char promotes only to int, and there's no int version. So long and double are both standard conversions of equal rank, and the call is ambiguous.
Key Takeaways
Overloading Rules in Summary
To summarize. Overloads must differ in their parameter types, number, or order. Return types and parameter names don't count. The compiler prefers an exact match, then a promotion, then a conversion. Two candidates of equal rank are ambiguous, so make the call exact, or redesign the overloads. And when the versions are the same operation with a fallback, use a default argument instead.
About C++ Programming at UPI Study
C++ Programming (Computer Science 242) is a self-paced online course of 84 video lessons in 11 chapters, from your first program to decisions, loops, arrays, functions, classes, files, pointers and inheritance. It carries a NCCRS credit recommendation for 3 semester credits.
Every lesson transcript is free on pages like this one, and the videos are free with a free UPI Study account. Enrolling adds a practice quiz after each lesson, the graded work and final exam that earn the credit, lifetime access, and an official transcript on completion. UPI Study has helped over 130,000 students with college credits. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Enroll now → Course details Download the syllabus Every course with PRO →
Why take it here
Why students take C++ Programming with UPI Study
Where the credit goes
Universities that work with NCCRS
C++ Programming carries a NCCRS credit recommendation. These universities work with NCCRS; some are UPI Study's signed partners. ACE and NCCRS have over 2,100 cooperating colleges that will consider accepting credits; the final decision is with the universities.
Thomas Edison State UniversitySigned partner
University of the PeopleSigned partner
Charter Oak State CollegeSigned partner
Strayer UniversitySigned partner
Columbia Southern UniversitySigned partner
Indiana TechSigned partner
Penn StateWorks with NCCRS
Southern New Hampshire UniversityWorks with NCCRS
SUNYWorks with NCCRS
+many more cooperating universitiesFind my college →Find my college → Not listed? Ask Athena in the corner of this page, or book a 15-minute call.
FAQ
Questions students ask us
About Function Overloading in Action
What makes two C++ functions count as different overloads?
Why can't C++ overload functions based only on return type?
How does the C++ compiler decide which overloaded function to call?
Why do I get an "ambiguous call" error with overloaded functions, and how do I fix it?
About the C++ Programming course
How many college credits is C++ Programming?
How much does C++ Programming cost and what is included?
Which universities accept UPI Study credits?
How long does C++ Programming take?
How are the exams done?
How do my credits reach my college?
Ready for the full course? Earn 3 credits in C++ Programming.
84 video lessons, a quiz after each one, and the graded work that earns the credit. Recommended for credit by NCCRS.
3 credits · NCCRSEnroll →