Menu

C++ Function Overloading: Same Name, Different Parameters

C++ function overloading lets several functions share one name as long as their parameter lists differ. Learn how overload resolution picks the match, why return type alone doesn't count, and the ambiguity and default-argument traps to avoid.

This page includes runnable editors - edit, run, and see output instantly.

One Name, Many Versions

You often need the same operation for different kinds of data: print an int, print a string, print a double. In some languages you'd invent printInt, printString, printDouble. C++ lets you give all of them the same name and tells them apart by their parameters. That's function overloading.

The rule is simple: several functions may share a name as long as their parameter lists differ - in the number of parameters, their types, or their order. The compiler looks at the arguments at each call site and picks the matching version for you.

Three functions, one name. Each call lands on the version whose parameter type fits the argument. This is what makes std::cout << x work for ints, doubles, and strings alike - operator<< is overloaded many times over.

What Counts as a Different Overload

An overload is distinguished by its parameter list only. You can vary:

int  area(int side);                 // 1 param
int  area(int width, int height);    // 2 params  -> different
double area(double r);               // different type -> different

void log(string msg, int level);     // order matters...
void log(int level, string msg);     // ...so this is different too

Each of those is a legal, distinct overload. The compiler builds a candidate set from all functions named area, then matches by argument count and type.

Return Type Alone Is Not Enough

Here's the gotcha that trips up almost everyone: you cannot overload on return type. The return value plays no part in choosing the overload, because the compiler decides which function to call from the arguments - before it ever looks at what comes back.

int    convert(double x);   // OK
double convert(double x);   // ERROR: redefinition - only the return type differs

That won't compile. If the parameter lists are identical, the two declarations are the same function as far as overloading is concerned, and you get a redefinition error. To branch on the result type, change a parameter (or use templates / a type cast at the call site instead).

How Overload Resolution Picks a Winner

When you make a call, the compiler ranks every viable overload and chooses the best match. Roughly, it prefers, in order:

  1. An exact match (no conversion needed).
  2. A promotion (e.g. char or short -> int, float -> double).
  3. A standard conversion (e.g. int -> double, double -> int, pointer-to-base).

If exactly one overload is strictly better than all others, that one wins. Watch how an exact match beats a conversion:

'A' is a char, but a promotion to int outranks a conversion to double, so it calls the int overload. These ranking rules are why overload resolution usually "just does the right thing" - and occasionally surprises you.

The Ambiguity Trap

If two overloads are equally good - neither strictly better - the compiler refuses to guess and reports an ambiguous call. The textbook case is two overloads that each need a same-rank conversion:

void f(int x);
void f(double x);

f(0L);   // ERROR: ambiguous - long -> int and long -> double are equal-rank conversions

Neither int nor double is an exact match for long, and both conversions sit at the same rank, so the call is ambiguous. You have two clean fixes:

A related surprise: passing a string literal. void g(const string&) and void g(bool) will both bid for g("hi"), and bool can win, because a const char* converts to bool (non-null -> true) with fewer steps than building a std::string. If you ever see a string literal mysteriously calling your bool overload, this is why - add a const char* or const string& overload to take the exact match.

Overloading and Default Arguments Don't Mix Well

Default arguments are not a substitute for overloading, and combining the two creates ambiguity. Each can answer the same call, so the compiler can't choose:

void connect(string host, int port = 8080);   // can be called with 1 arg
void connect(string host);                     // also callable with 1 arg

connect("localhost");   // ERROR: ambiguous - both match a single argument

Pick one approach per call shape. Use default arguments when the behavior is identical and you just want optional parameters; use overloading when the different argument lists should run genuinely different code. Mixing them so that two signatures collide for the same call count is a guaranteed ambiguity error.

One more distinction worth nailing down: overloading is not overriding. Overloading resolves at compile time among functions in the same scope with the same name but different parameters. Overriding replaces a virtual function in a derived class at run time and requires the same signature - a topic for virtual functions later.

Next: Lambdas

Overloading gives one name several typed implementations chosen at compile time. Sometimes, though, you don't want a named function at all - you need a tiny throwaway function defined right where you use it, often to pass into an algorithm like sort. That's exactly what lambdas are: anonymous functions you can write inline, capture surrounding variables with, and hand off in a single expression. Next we'll see how to write them and when they beat a full named function.

Frequently Asked Questions

What is function overloading in C++?

Function overloading lets you define multiple functions with the same name as long as their parameter lists differ (in number, type, or order). The compiler picks which one to call based on the arguments you pass - so print(42) and print("hi") can call two different print functions.

Can two C++ functions differ only by return type?

No. Overloads must differ in their parameter list. int f(int) and double f(int) are a compile error - the return type is not part of the signature used for overload resolution, because the compiler chooses the overload from the arguments at the call site, before the return value is ever used.

What causes an "ambiguous call" error with overloaded functions?

It happens when two overloads are equally good matches and the compiler can't prefer one. A classic case is f(int) and f(double) called with f(0L) (a long), where both require a conversion of equal rank. Fix it by adding an exact-match overload or casting the argument to the type you want.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED