Two functions can share a name, and one function can accept fewer written arguments, but these are different C++ mechanisms. The compiler's choice is not always obvious from the call. One runnable invoice program settles the difference: its four calls print 1000.00, 1180.00, 1050.00, and 1080.00, and each of those numbers comes from a different resolution decision. Declarations carry their own validity rules, and two reasonable-looking overloads can leave an ordinary call refusing to compile. All commands use a C++17-capable g++ toolchain. Diagnostic wording may vary across compilers.
Overloading and default arguments solve different problems
Function overloading lets functions in one scope share a name when their parameter lists are distinguishable. Given print(int) and print(double), print(7) selects the first, while print(7.5) selects the second. This choice happens at compile time.
A default argument supplies an omitted trailing value. With void greet(std::string name, std::string prefix = "Hello"), greet("Mira") uses "Hello"; greet("Mira", "Welcome") supplies both values. This is one two-parameter function, not two overloads.
Memory test: overloading asks, "Which declaration wins?" Defaults ask, "Which trailing values may the caller omit?" Wider C++ and DSA study sits in the Coding & DSA category, and if declaring and calling functions still feels shaky, revise that first with C Programming for Teaching CS Exams.
Run one program that combines both mechanisms
Save as invoice.cpp:
#include <iomanip>
#include <iostream>
double invoiceTotal(int unitPrice, int quantity) {
return unitPrice * quantity;
}
double invoiceTotal(double subtotal,
double taxRate = 0.18,
double discount = 0.0) {
return subtotal + subtotal * taxRate - discount;
}
int main() {
std::cout << std::fixed << std::setprecision(2);
std::cout << "Bulk total: "
<< invoiceTotal(250, 4) << '\n';
std::cout << "Default tax: "
<< invoiceTotal(1000.0) << '\n';
std::cout << "Custom tax: "
<< invoiceTotal(1000.0, 0.05) << '\n';
std::cout << "Tax and discount: "
<< invoiceTotal(1000.0, 0.18, 100.0) << '\n';
}Build with g++ -std=c++17 -Wall -Wextra -pedantic invoice.cpp -o invoice, then run ./invoice:
Bulk total: 1000.00
Default tax: 1180.00
Custom tax: 1050.00
Tax and discount: 1080.00Trace every result. The exact int, int overload gives 250 * 4 = 1000. The one-double call supplies 0.18 and 0.0: 1000 + 1000 * 0.18 - 0 = 1180. Two doubles give 1000 + 50 - 0 = 1050. Three doubles give 1000 + 180 - 100 = 1080.
How C++ selects the best overload
Use three passes:
Collect same-name candidates in scope.
Discard functions that cannot accept the supplied arguments, including permitted defaults.
Rank viable candidates by conversions for the arguments actually supplied.
An exact match beats a promotion, which beats a broader standard conversion, and matching only through an ellipsis parameter ranks worst of all. If two candidates still tie on conversions, a non-template function wins over a template instantiation.
For (250, 4), both overloads are viable, but int, int gets two exact matches; the other needs two conversions. For (1000.0, 0.05), the double overload gets exact matches. Three arguments make the integer overload non-viable. Given label(int) and label(double), char grade = 'A'; label(grade); selects label(int): char to int is a promotion, while char to double is a conversion. Source order does not break an equal tie.

Which declarations form valid overloads
void parse(int value);, void parse(double value);, and void parse(int value, int base); are valid because type or count differs. So are void inspect(int& value); and void inspect(const int& value);: an int n = 9 prefers the first, while a const int n = 9 binds only to the second.
Return type alone cannot distinguish int convert(double value); from double convert(double value);; parameter names cannot either. Similarly, void save(int value); and void save(const int value); declare the same function because top-level const on a by-value parameter does not change its type. Two bodies cause redefinition. In int& versus const int&, the referred-to type does affect binding.
Default-argument rules that prevent surprising calls
Place defaults in a declaration visible before the call, then omit them from the later definition:
double invoiceTotal(double subtotal,
double taxRate = 0.18,
double discount = 0.0);
double invoiceTotal(double subtotal,
double taxRate,
double discount) {
return subtotal + subtotal * taxRate - discount;
}Repeating a default in the same-scope definition redefines it. Calls use defaults visible in their declaration context.
Once a parameter has a default, every parameter to its right must have one at that declaration point. Therefore void retry(int attempts = 3, int delaySeconds); is ill-formed; void retry(int attempts, int delaySeconds = 10); is valid. There is no blank placeholder: write invoiceTotal(1000.0, 0.18, 100.0), never invoiceTotal(1000.0, , 100.0).
Default expressions run on each omitting call. Use int nextTicket() { static int id = 40; return ++id; } and void printTicket(int id = nextTicket()) { std::cout << id << '\n'; }. Two printTicket(); calls print 41, then 42; printTicket(90); prints 90 without calling nextTicket. A defaulted parameter is therefore not a constant: whatever the expression can observe may change between calls.
Ambiguities and traps: why they happen and how to repair them
Given void alert(int code); and void alert(int code, bool urgent = false);, alert(7) is ambiguous: both have an exact int match, and fewer defaults do not win. Remove an overload or the default, use distinct names, or call alert(7, true).
With void pick(long value); and void pick(double value);, pick(10) is ambiguous because both need same-rank standard conversions. Use pick(10L) or pick(10.0). With void send(int*); and void send(double*);, send(nullptr) is ambiguous; send(static_cast<int*>(nullptr)) selects the first.
Overloading selects same-name functions at compile time. Overriding uses derived-class implementations and virtual dispatch. Operator overloading defines operator meaning for eligible types. OOP Concepts for Teaching CS Exams covers the inheritance and virtual-dispatch side of that comparison.

How exams test overloading and default arguments
Exam questions ask you to pick the winning overload, judge whether two declarations are distinguishable, supply omitted trailing arguments, spot the line that will not compile, or repair an ambiguous call. Our Function & Operator Overloading question set carries over 10 questions, weighted toward operator overloading and signature rules rather than defaults. Attempt each of these before reading its answer.
int score(int base, int bonus = 5)adds;double score(double base)doubles. Attempt first. Answer:score(20)selectsintand returns25;score(20.0)selectsdoubleand returns40.0.Given
route(int),route(long), andshort stop = 12, predictroute(stop). Answer:route(int), becauseshorttointis a promotion;shorttolongis a conversion.Is
void retry(int attempts = 3, int delaySeconds);legal? Answer after attempting: no, because a non-defaulted parameter follows a defaulted one. Repair it asvoid retry(int attempts, int delaySeconds = 10);;retry(3)then supplies10.For both
alertdeclarations, name both viable candidates before answering. Answer:alert(7)is ambiguous, so the file never compiles and the program never runs;alert(7, true)selects the two-parameter version.
Change the invoice default tax to 0.12. The one-double call gives 1000 + 120 = 1120.00; explicit 0.05 still gives 1000 + 50 = 1050.00. This separates defaults from supplied arguments.
Function overloading and defaults: the short version and next step
Five rules matter: parameters distinguish overloads, not return types; defaults shorten calls to one function; only trailing arguments may be omitted; supplied arguments determine ranking; equal viable candidates cause ambiguity. Remember 250 * 4 = 1000.00 and 1000 + 1000 * 0.18 - 100 = 1080.00.
Compile all four lines. Comment out the integer overload: (250, 4) reaches the double version, treats 4 as taxRate = 4.0, defaults discount = 0.0, and prints 1250.00. Restore it, add both alert declarations, compile alert(7), then repair it as alert(7, true). Change one line at a time and read the listed candidates.
Continue with the C++ Programming course. For broader multi-language practice, use Coding For Placements.




