Menu

C++ Funktionsüberladung: gleicher Name, andere Parameter

Die Funktionsüberladung in C++ erlaubt es mehreren Funktionen, sich einen Namen zu teilen, solange sich ihre Parameterlisten unterscheiden. Lerne, wie die Überladungsauflösung die passende Variante wählt, warum der Rückgabetyp allein nicht zählt und welche Fallen bei Mehrdeutigkeit und Standardargumenten du vermeiden solltest.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Ein Name, viele Varianten

Oft brauchst du dieselbe Operation für verschiedene Datenarten: einen int ausgeben, einen string ausgeben, einen double ausgeben. In manchen Sprachen würdest du dir printInt, printString, printDouble ausdenken. C++ erlaubt es dir, ihnen allen denselben Namen zu geben, und unterscheidet sie anhand ihrer Parameter. Das ist Funktionsüberladung.

Die Regel ist einfach: Mehrere Funktionen dürfen sich einen Namen teilen, solange sich ihre Parameterlisten unterscheiden - in der Anzahl der Parameter, ihren Typen oder ihrer Reihenfolge. Der Compiler betrachtet die Argumente an jeder Aufrufstelle und wählt für dich die passende Variante.

Drei Funktionen, ein Name. Jeder Aufruf landet bei der Variante, deren Parametertyp zum Argument passt. Genau das lässt std::cout << x für ints, doubles und strings gleichermaßen funktionieren - operator<< ist viele Male überladen.

Was als eigene Überladung zählt

Eine Überladung wird allein durch ihre Parameterliste unterschieden. Du kannst variieren:

int  area(int side);                 // 1 Parameter
int  area(int width, int height);    // 2 Parameter  -> anders
double area(double r);               // anderer Typ -> anders

void log(string msg, int level);     // die Reihenfolge zählt...
void log(int level, string msg);     // ...also ist auch das anders

Jede davon ist eine gültige, eigenständige Überladung. Der Compiler bildet aus allen Funktionen namens area eine Menge von Kandidaten und gleicht dann nach Argumentanzahl und -typ ab.

Der Rückgabetyp allein reicht nicht

Hier ist die Falle, über die fast jeder stolpert: Du kannst nicht über den Rückgabetyp überladen. Der Rückgabewert spielt bei der Wahl der Überladung keine Rolle, denn der Compiler entscheidet anhand der Argumente, welche Funktion aufgerufen wird - lange bevor er überhaupt darauf schaut, was zurückkommt.

int    convert(double x);   // OK
double convert(double x);   // FEHLER: Neudefinition - nur der Rückgabetyp unterscheidet sich

Das kompiliert nicht. Sind die Parameterlisten identisch, sind die beiden Deklarationen aus Sicht der Überladung dieselbe Funktion, und du bekommst einen Neudefinitionsfehler. Um anhand des Ergebnistyps zu verzweigen, ändere einen Parameter (oder nutze Templates / static_cast an der Aufrufstelle).

Wie die Überladungsauflösung einen Sieger kürt

Wenn du einen Aufruf machst, ordnet der Compiler alle in Frage kommenden Überladungen ein und wählt die beste Übereinstimmung. Grob bevorzugt er in dieser Reihenfolge:

  1. Eine exakte Übereinstimmung (keine Umwandlung nötig).
  2. Eine Promotion (z. B. char oder short -> int, float -> double).
  3. Eine Standardumwandlung (z. B. int -> double, double -> int, Zeiger auf Basisklasse).

Ist genau eine Überladung strikt besser als alle anderen, gewinnt diese. Sieh dir an, wie eine exakte Übereinstimmung eine Umwandlung schlägt:

'A' ist ein char, doch eine Promotion zu int rangiert über einer Umwandlung zu double, also wird die int-Überladung aufgerufen. Diese Rangregeln sind der Grund, warum die Überladungsauflösung meist "einfach das Richtige tut" - und dich gelegentlich überrascht.

Die Mehrdeutigkeitsfalle

Sind zwei Überladungen gleich gut - keine strikt besser - weigert sich der Compiler zu raten und meldet einen mehrdeutigen Aufruf. Der Lehrbuchfall sind zwei Überladungen, die jeweils eine Umwandlung gleichen Rangs benötigen:

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

f(0L);   // FEHLER: mehrdeutig - long -> int und long -> double sind ranggleiche Umwandlungen

Weder int noch double ist eine exakte Übereinstimmung für long, und beide Umwandlungen liegen auf demselben Rang, also ist der Aufruf mehrdeutig. Du hast zwei saubere Lösungen:

Eine verwandte Überraschung: das Übergeben eines String-Literals. void g(const string&) und void g(bool) bewerben sich beide um g("hi"), und bool kann gewinnen, weil sich ein const char* in weniger Schritten zu bool umwandelt (nicht-null -> true), als ein std::string zu konstruieren. Wenn du je siehst, dass ein String-Literal rätselhafterweise deine bool-Überladung aufruft, ist das der Grund - ergänze eine const char*- oder const string&-Überladung, damit sie die exakte Übereinstimmung übernimmt.

Überladung und Standardargumente vertragen sich schlecht

Standardargumente sind kein Ersatz für Überladung, und beides zu kombinieren erzeugt Mehrdeutigkeit. Jede kann denselben Aufruf bedienen, also kann der Compiler nicht wählen:

void connect(string host, int port = 8080);   // mit 1 Argument aufrufbar
void connect(string host);                     // ebenfalls mit 1 Argument aufrufbar

connect("localhost");   // FEHLER: mehrdeutig - beide passen zu einem einzelnen Argument

Wähle pro Aufrufform einen einzigen Ansatz. Nutze Standardargumente, wenn das Verhalten identisch ist und du einfach optionale Parameter willst; nutze Überladung, wenn die unterschiedlichen Argumentlisten wirklich verschiedenen Code ausführen sollen. Sie so zu mischen, dass zwei Signaturen für dieselbe Argumentanzahl kollidieren, ist ein garantierter Mehrdeutigkeitsfehler.

Noch eine Unterscheidung, die es festzuhalten lohnt: Überladung ist nicht Überschreiben. Überladung wird zur Kompilierzeit unter Funktionen im selben Gültigkeitsbereich mit gleichem Namen, aber unterschiedlichen Parametern aufgelöst. Überschreiben ersetzt eine virtual-Funktion in einer abgeleiteten Klasse zur Laufzeit und erfordert die gleiche Signatur - ein Thema für virtuelle Funktionen später.

Als Nächstes: Lambdas

Überladung gibt einem Namen mehrere typisierte Implementierungen, die zur Kompilierzeit gewählt werden. Manchmal willst du aber gar keine benannte Funktion - du brauchst eine winzige Wegwerf-Funktion, genau dort definiert, wo du sie verwendest, oft um sie an einen Algorithmus wie sort zu übergeben. Genau das sind Lambdas: anonyme Funktionen, die du inline schreiben, mit denen du umliegende Variablen einfängst und die du in einem einzigen Ausdruck weiterreichst. Als Nächstes sehen wir, wie man sie schreibt und wann sie eine vollständige benannte Funktion schlagen.

Häufig gestellte Fragen

Was ist Funktionsüberladung in C++?

Mit Funktionsüberladung kannst du mehrere Funktionen mit demselben Namen definieren, solange sich ihre Parameterlisten unterscheiden (in Anzahl, Typ oder Reihenfolge). Der Compiler wählt anhand der übergebenen Argumente aus, welche aufgerufen wird - so können print(42) und print("hi") zwei verschiedene print-Funktionen aufrufen.

Können sich zwei C++-Funktionen nur im Rückgabetyp unterscheiden?

Nein. Überladungen müssen sich in ihrer Parameterliste unterscheiden. int f(int) und double f(int) sind ein Compilerfehler - der Rückgabetyp ist nicht Teil der für die Überladungsauflösung verwendeten Signatur, weil der Compiler die Überladung aus den Argumenten an der Aufrufstelle wählt, noch bevor der Rückgabewert überhaupt verwendet wird.

Was verursacht einen "mehrdeutigen Aufruf"-Fehler bei überladenen Funktionen?

Er entsteht, wenn zwei Überladungen gleich gut passen und der Compiler keine bevorzugen kann. Ein klassischer Fall ist f(int) und f(double), aufgerufen mit f(0L) (einem long), wobei beide eine Umwandlung gleichen Rangs erfordern. Behebe es, indem du eine Überladung mit exakter Übereinstimmung hinzufügst oder das Argument in den gewünschten Typ castest.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S