In der Programmiersprache C++ ist das Zurückgeben mehrerer Werte aus einer Funktion eine immer wiederkehrende Herausforderung. Klassische Ansätze mussten oft mit Umwegen wie der Verwendung von Zeigern oder Referenzen arbeiten, um mehrere Ergebnisse zu übermitteln. Mit der Weiterentwicklung des C++-Standards, insbesondere durch die Neuerungen in C++17 und C++23, ist dieser Prozess deutlich eleganter und komfortabler geworden. Moderne Funktionen und Datentypen erlauben es Entwicklern, mehrere Rückgabewerte auf unkomplizierte und lesbare Weise zu handhaben, was die Codequalität und Wartbarkeit erheblich verbessert. In diesem Beitrag werden die wichtigsten Methoden zum Rückgeben mehrerer Werte in C++ vorgestellt, von einfachen Paaren bis hin zur robusten Fehlerbehandlung mit std::expected.
Der wohl bekannteste Weg, um zwei Werte aus einer Funktion zurückzugeben, führt über die Nutzung von std::pair. Dieses in der Standardbibliothek verankerte Template erlaubt es, zwei heterogene Werte zusammenzufassen. Möchte man beispielsweise eine Division durchführen und sowohl das Ergebnis als auch eine Fehlernachricht zurückliefern, kann man eine Funktion schreiben, die ein std::pair<int, std::string> zurückgibt. Sollte der Divisor null sein, so liefert die Funktion eine Fehlermeldung, andernfalls das Ergebnis der Division und ein Erfolgsstring. Diese Methode ist einfach und klar, sie hat jedoch den Nachteil, dass sie nur zwei Werte kapseln kann und die Bedeutung der einzelnen Werte nicht durch eigene Typen oder Struktur hervorgehoben wird.
Für den Fall, dass mehr als zwei Werte zurückgegeben werden sollen, bietet sich std::tuple an. Dieses Template ist eine Art Container, der eine beliebige Anzahl an Werten unterschiedlicher Typen halten kann. Mit C++17 kam zudem die Unterstützung für strukturierte Bindungen, die es ermöglicht, eine solche Rückgabe direkt in verständliche Einzelvariablen zu zerlegen. Dadurch wird der Code wesentlich lesbarer, da die einzelnen Rückgabewerte explizit benannt werden können. Ein typisches Anwendungsbeispiel ist erneut die Fehlerbehandlung bei Berechnungen: Die Funktion liefert ein std::tuple mit Ergebnis, Statuscode und einer eventuellen Meldung.
Eine häufige Fragestellung bei der Rückgabe mehrerer Werte in C++ betrifft auch die flexible Handhabung von Datentypen. Indem man Templates und Konzepte nutzt, kann die Funktion so gestaltet werden, dass sie für verschiedene Datentypen funktioniert, ohne dass man sie mehrfach überladen muss. C++20 bringt die Möglichkeit, mit Konzepten zu arbeiten, die zur Compilezeit sicherstellen, dass nur passende Typen übergeben werden. Das sorgt für klaren und sicheren Code. So kann eine Division beispielsweise für alle ganzzahligen Typen definiert werden, die das Konzept std::integral erfüllen.
Versucht man eine Division mit Gleitkommazahlen, erfolgt ein aussagekräftiger Compilerfehler. C++23 hat weitere Verbesserungen gebracht, die für das Thema der Rückgabe mehrerer Werte relevant sind. Die neue Funktion std::println macht die Ausgabe von Werten sehr einfach und zugleich effizient, da sie formatierte Ausgaben direkt erlaubt. Zudem wurde das sogenannte "Ignorieren von Rückgabewerten" dank der Einführung des Unterstrichs (_) in strukturierte Bindungen vereinfacht. Wenn ein Rückgabewert nicht interessiert, kann er mit _ abgefangen werden, ohne eine unnötige Variable deklarieren zu müssen – das steigert die Klarheit und verhindert ungenutzte Variablen.
Neben den klassischen Containern ist die deklarative Fehlerbehandlung ein zentraler Aspekt bei der Rückgabe mehrerer Werte. Bislang wurde dafür oft die Verwendung von Ausnahmen (Exceptions) propagiert, was in vielen Kontexten jedoch zu weniger übersichtlichem und schwer wartbarem Code führt. Die neu eingeführte Klasse std::expected (seit C++23) bietet eine elegante Alternative: Sie erlaubt es, entweder einen Wert oder eine Fehlermeldung zurückzugeben, während der Aufrufer klar und einfach prüfen kann, ob die Operation erfolgreich war. Bei Fehlern kann die Fehlermeldung abgefragt werden, bei Erfolg der korrekte Wert. Diese Vorgehensweise ist stark von funktionalen Sprachen oder der Programmiersprache Rust inspiriert und fördert das Schreiben von robustem Code.
Die Nutzung von std::expected bietet neben besserer Lesbarkeit auch Performancevorteile, da die Ausnahmebehandlung entfällt. Stattdessen handelt es sich um einen normalen Rückgabewert, der situationsabhängig verwertet wird. Dadurch wird der häufig kritisierte Overhead von Ausnahmen umgangen, was besonders in Systemprogrammen und sicherheitskritischen Anwendungen von großer Bedeutung ist. Eine Alternative zu std::expected stellt std::optional dar. Dieser Container kann entweder einen Wert enthalten oder leer sein.
Das eignet sich besonders, wenn nur der Wert selbst optional ist, ohne eine detaillierte Fehlerbeschreibung zu liefern. Die Flexibilität von std::optional macht es zu einem nützlichen Werkzeug für einfache Fälle, allerdings ist es geringfügig weniger aussagekräftig als std::expected, wenn es um Fehlerhandling geht. Für Situationen, in denen lediglich angegeben werden muss, ob ein Wert vorhanden ist oder nicht, reicht std::optional aus. Zusammenfassend lässt sich sagen, dass die Möglichkeiten, in C++ mehrere Werte aus einer Funktion zurückzugeben, sich dank moderner Sprachstandards erheblich erweitert haben. Vom einfachen std::pair über flexible std::tuple, strukturierte Bindungen, und die Einführung von std::expected und std::optional bis hin zu syntaktischen Erleichterungen wie dem Ignorieren von Rückgabewerten – diese Werkzeuge erlauben es Entwicklern, klaren, effizienten und robusten Code zu schreiben.
Damit lösen sich alte Probleme im Umgang mit Funktionsrückgaben auf und neue Anwendungsbereiche werden spielend leicht abgedeckt. Die Wahl der richtigen Technik hängt dabei stark vom jeweiligen Anwendungsfall ab. Für einfache Kombinationen von zwei oder drei Werten sind std::pair und std::tuple bestens geeignet. Bei komplexerer Fehlerbehandlung sollten std::expected oder std::optional bevorzugt werden. Neben diesen modernen Ansätzen ist es wichtig, den Code stets lesbar und wartbar zu halten, sodass spätere Entwickler oder man selbst die Logik schnell nachvollziehen kann.
Abschließend lässt sich festhalten, dass C++ mit jeder Sprachversion ein Stück benutzerfreundlicher wird. Die Möglichkeit, mehrere Werte mit minimalem Aufwand zurückzugeben, führt zu schlankeren Schnittstellen, weniger Fehlerquellen und besser strukturierter Fehlerbehandlung. Für alle Programmierer, die auf Performance, Sicherheit und sauberen Code Wert legen, empfiehlt es sich, sich mit diesen Techniken auseinanderzusetzen und sie in eigenen Projekten zu nutzen. Das Resultat ist ein moderner C++-Code, der den Ansprüchen heutiger Softwareentwicklung mehr als gerecht wird.