Mehrere Werte aus einer C++-Funktion zurückgeben

Lesezeit: 10 Minuten

Mehrere Werte aus einer C Funktion zuruckgeben
Fred Larson

Gibt es eine bevorzugte Möglichkeit, mehrere Werte aus einer C++-Funktion zurückzugeben? Stellen Sie sich zum Beispiel eine Funktion vor, die zwei ganze Zahlen dividiert und sowohl den Quotienten als auch den Rest zurückgibt. Eine Möglichkeit, die ich häufig sehe, ist die Verwendung von Referenzparametern:

void divide(int dividend, int divisor, int& quotient, int& remainder);

Eine Variation besteht darin, einen Wert zurückzugeben und den anderen durch einen Referenzparameter zu übergeben:

int divide(int dividend, int divisor, int& remainder);

Eine andere Möglichkeit wäre, eine Struktur zu deklarieren, die alle Ergebnisse enthält, und Folgendes zurückzugeben:

struct divide_result {
    int quotient;
    int remainder;
};

divide_result divide(int dividend, int divisor);

Wird einer dieser Wege im Allgemeinen bevorzugt oder gibt es andere Vorschläge?

Bearbeiten: Im realen Code kann es mehr als zwei Ergebnisse geben. Sie können auch von unterschiedlicher Art sein.

Mehrere Werte aus einer C Funktion zuruckgeben
rauben

Um zwei Werte zurückzugeben, verwende ich a std::pair (normalerweise typedef’d). Sollte man sich ansehen boost::tuple (in C++11 und neuer gibt es std::tuple) für mehr als zwei Rückgabeergebnisse.

Mit Einführung der strukturierten Bindung in C++ 17, Rückkehr std::tuple dürfte wohl zum Standard werden.

  • +1 für Tupel. Beachten Sie die Auswirkungen auf die Leistung von großen Objekten, die in einer Struktur zurückgegeben werden, im Vergleich zur Übergabe als Referenz.

    – Marcin

    26. November 2008 um 15:40 Uhr

  • Wenn Sie Tupel verwenden, warum verwenden Sie sie nicht auch für Paare. Warum einen Sonderfall?

    – Ferruccio

    26. November 2008 um 15:44 Uhr

  • Fred, ja boost::tuple kann das 🙂

    – Johannes Schaub – litb

    26. November 2008 um 15:51 Uhr

  • In C++11 können Sie verwenden std::tuple.

    – Ferruccio

    20. Oktober 2011 um 10:32 Uhr

  • Wenn Sie mehrere Werte von einer Funktion akzeptieren möchten, können Sie dies bequem mit verwenden std::tie stackoverflow.com/a/2573822/502144

    – fdermishin

    12. Mai 2014 um 8:44 Uhr

1647208813 75 Mehrere Werte aus einer C Funktion zuruckgeben
pfeffer_chico

In C++11 können Sie:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  std::make_tuple(dividend / divisor, dividend % divisor);
}

#include <iostream>

int main() {
    using namespace std;

    int quotient, remainder;

    tie(quotient, remainder) = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

In C++17:

#include <tuple>

std::tuple<int, int> divide(int dividend, int divisor) {
    return  {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

oder mit structs:

auto divide(int dividend, int divisor) {
    struct result {int quotient; int remainder;};
    return result {dividend / divisor, dividend % divisor};
}

#include <iostream>

int main() {
    using namespace std;

    auto result = divide(14, 3);

    cout << result.quotient << ',' << result.remainder << endl;

    // or

    auto [quotient, remainder] = divide(14, 3);

    cout << quotient << ',' << remainder << endl;
}

  • Ich habe ein Problem mit Funktionen, die Tupel zurückgeben. Angenommen, der obige Funktionsprototyp befindet sich in einem Header. Woher weiß ich dann, was der erste und der zweite zurückgegebene Wert bedeuten, ohne die Funktionsdefinition zu verstehen? Quotient-Rest oder Rest-Quotient.

    – Uchia Itachi

    4. November 2016 um 7:22 Uhr

  • @UchiaItachi Gleiche Sorge um Funktionsparameter, Sie können ihnen Namen geben, aber die Sprache erzwingt das nicht einmal, und die Parameternamen haben beim Lesen auf der Aufrufseite keinen Wert. Außerdem haben Sie bei einer einzigen Rückkehr nur einen Typ, aber den Namen zu haben, könnte auch nützlich sein, mit Tupeln verdoppeln Sie nur das Problem, also fehlt der Sprache meiner Meinung nach einfach die Selbstdokumentation auf verschiedene Weise, nicht nur das.

    – pfeffer_chico

    20. Januar 2017 um 15:21 Uhr


  • Wie würde das letzte Beispiel aussehen, wenn ich den Rückgabetyp von divide() explizit angeben wollte? Soll ich das Ergebnis dann woanders definieren oder kann ich es direkt in der Rückgabetypspezifikation definieren?

    – Slava

    11. Juli 2017 um 13:30 Uhr

  • @Slava Sie können einen Typ nicht direkt in der Funktionssignatur definieren, Sie müssten den Typ außerhalb deklarieren und als Rückgabetyp verwenden, wie es normalerweise der Fall ist (verschieben Sie einfach die struct Zeile außerhalb des Funktionskörpers und ersetzen auto Funktion zurück mit result.

    – pfeffer_chico

    11. Juli 2017 um 15:43 Uhr


  • @pepper_chico Was ist, wenn Sie die Funktionsdefinition von setzen möchten divide in eine separate cpp-Datei? Ich bekomme den Fehler error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. Wie löse ich das?

    – Adrian

    23. Februar 2018 um 20:26 Uhr

Mehrere Werte aus einer C Funktion zuruckgeben
Fred Larson

Ich persönlich mag Rückgabeparameter im Allgemeinen aus mehreren Gründen nicht:

  • es ist beim Aufruf nicht immer offensichtlich, welche Parameter Ins und welche Outs sind
  • Sie müssen im Allgemeinen eine lokale Variable erstellen, um das Ergebnis abzufangen, während Rückgabewerte inline verwendet werden können (was eine gute Idee sein kann oder nicht, aber Sie haben zumindest die Möglichkeit).
  • Es scheint mir sauberer zu sein, eine “Eingangstür” und eine “Ausgangstür” für eine Funktion zu haben – alle Eingänge gehen hier hinein, alle Ausgänge kommen dort heraus
  • Ich halte meine Argumentationsliste gerne so kurz wie möglich

Ich habe auch einige Vorbehalte gegenüber der Paar/Tupel-Technik. Vor allem gibt es oft keine natürliche Reihenfolge der Rückgabewerte. Wie soll der Leser des Codes wissen, ob result.first ist der Quotient oder der Rest? Und der Implementierer könnte die Reihenfolge ändern, was den vorhandenen Code beschädigen würde. Dies ist besonders heimtückisch, wenn die Werte denselben Typ haben, sodass kein Compilerfehler oder keine Warnung generiert wird. Tatsächlich gelten diese Argumente auch für Rückgabeparameter.

Hier ist ein weiteres Codebeispiel, dieses etwas weniger trivial:

pair<double,double> calculateResultingVelocity(double windSpeed, double windAzimuth,
                                               double planeAirspeed, double planeCourse);

pair<double,double> result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.first << endl;
cout << result.second << endl;

Druckt dies Grundgeschwindigkeit und Kurs oder Kurs und Grundgeschwindigkeit? Es ist nicht offensichtlich.

Vergleich dazu:

struct Velocity {
    double speed;
    double azimuth;
};
Velocity calculateResultingVelocity(double windSpeed, double windAzimuth,
                                    double planeAirspeed, double planeCourse);

Velocity result = calculateResultingVelocity(25, 320, 280, 90);
cout << result.speed << endl;
cout << result.azimuth << endl;

Ich denke, das ist übersichtlicher.

Daher denke ich, dass meine erste Wahl im Allgemeinen die Struct-Technik ist. Die Paar/Tupel-Idee ist wahrscheinlich in bestimmten Fällen eine großartige Lösung. Ich möchte die Rückgabeparameter möglichst vermeiden.

  • Der Vorschlag, a struct wie Velocity ist eine nette. Eine Sorge ist jedoch, dass es den Namensraum verschmutzt. Ich nehme an, dass mit C++11 die struct kann einen langen Typnamen haben, und man kann verwenden auto result = calculateResultingVelocity(...).

    – Hugues

    12. Februar 2013 um 5:06 Uhr


  • +1. Eine Funktion sollte zurückkehren ein “Ding”, kein irgendwie geordnetes “Tupel von Dingen”.

    – DevSolar

    13. Mai 2013 um 7:12 Uhr

  • Ich bevorzuge Strukturen gegenüber std::pairs/std::tuples aus den in dieser Antwort beschriebenen Gründen. Aber ich mag den Namensraum “Verschmutzung” auch nicht. Die ideale Lösung für mich wäre die Rückgabe anonymer Strukturen wie struct { int a, b; } my_func();. Dies könnte wie folgt verwendet werden: auto result = my_func();. Aber C++ erlaubt dies nicht: “Neue Typen dürfen nicht in einem Rückgabetyp definiert werden”. Also muss ich Strukturen wie erstellen struct my_func_result_t

    – anton_rh

    21. April 2016 um 6:14 Uhr


  • @anton_rh : C ++ 14 ermöglicht die Rückgabe lokaler Typen mit autodamit auto result = my_func(); ist trivial erhältlich.

    – Ildjarn

    15. Januar 2017 um 1:11 Uhr

  • Als wir vor etwa 15 Jahren Boost entdeckten, verwendeten wir häufig Tupel, da es ziemlich praktisch ist. Besonders bei Tupeln gleichen Typs (zB tuple; which one is which) haben wir mit der Zeit den Nachteil in der Lesbarkeit festgestellt. In letzter Zeit haben wir uns also eher angewöhnt, eine kleine POD-Struktur einzuführen, bei der zumindest der Name der Member-Variablen etwas Vernünftiges anzeigt.

    – Gast128

    14. August 2017 um 15:19 Uhr

std::pair<int, int> divide(int dividend, int divisor)
{
   // :
   return std::make_pair(quotient, remainder);
}

std::pair<int, int> answer = divide(5,2);
 // answer.first == quotient
 // answer.second == remainder

std::pair ist im Wesentlichen Ihre Strukturlösung, aber bereits für Sie definiert und bereit, sich an zwei beliebige Datentypen anzupassen.

Es hängt vollständig von der tatsächlichen Funktion und der Bedeutung der mehreren Werte und ihrer Größe ab:

  • Wenn sie wie in Ihrem Bruchbeispiel verwandt sind, würde ich mich für eine Struktur- oder Klasseninstanz entscheiden.
  • Wenn sie nicht wirklich verwandt sind und nicht in eine Klasse/Struktur gruppiert werden können, sollten Sie Ihre Methode vielleicht in zwei umgestalten.
  • Abhängig von der Speichergröße der zurückgegebenen Werte möchten Sie möglicherweise einen Zeiger auf eine Klasseninstanz oder Struktur zurückgeben oder Referenzparameter verwenden.

  • Ich mag Ihre Antwort und Ihre letzte Kugel erinnert mich an etwas, das ich gerade gelesen habe, dass das Passieren von Werten viel schneller geworden ist, abhängig von den Umständen, die dies komplizierter machen … cpp-next.com/archive/2009/08/want-speed-pass-by-value

    – Salbei

    26. Dezember 2012 um 19:29 Uhr

1647208813 609 Mehrere Werte aus einer C Funktion zuruckgeben
Bill K

Die OO-Lösung hierfür besteht darin, eine Verhältnisklasse zu erstellen. Es würde keinen zusätzlichen Code benötigen (würde etwas sparen), wäre erheblich sauberer/klarer und würde Ihnen einige zusätzliche Refactorings geben, mit denen Sie Code auch außerhalb dieser Klasse bereinigen können.

Eigentlich denke ich, dass jemand empfohlen hat, eine Struktur zurückzugeben, die nahe genug ist, aber die Absicht verbirgt, dass dies eine vollständig durchdachte Klasse mit Konstruktor und einigen Methoden sein muss, tatsächlich die “Methode”, die Sie ursprünglich erwähnt haben (als Rückgabe der pair) sollte höchstwahrscheinlich ein Mitglied dieser Klasse sein, die eine Instanz von sich selbst zurückgibt.

Ich weiß, dass Ihr Beispiel nur ein “Beispiel” war, aber Tatsache ist, dass, wenn Ihre Funktion nicht viel mehr tut, als jede Funktion tun sollte, wenn Sie möchten, dass sie mehrere Werte zurückgibt, Ihnen mit ziemlicher Sicherheit ein Objekt fehlt.

Scheuen Sie sich nicht, diese winzigen Klassen zu erstellen, um kleine Aufgaben zu erledigen – das ist die Magie von OO – Sie zerlegen es am Ende, bis jede Methode sehr klein und einfach und jede Klasse klein und verständlich ist.

Eine andere Sache, die ein Indikator dafür hätte sein sollen, dass etwas nicht stimmte: In OO haben Sie im Wesentlichen keine Daten – bei OO geht es nicht darum, Daten weiterzugeben, eine Klasse muss ihre eigenen Daten intern verwalten und manipulieren, alle Daten werden weitergegeben (einschließlich Zugriffsmethoden) ist ein Zeichen dafür, dass Sie möglicherweise etwas überdenken müssen.

  • Ich mag Ihre Antwort und Ihre letzte Kugel erinnert mich an etwas, das ich gerade gelesen habe, dass das Passieren von Werten viel schneller geworden ist, abhängig von den Umständen, die dies komplizierter machen … cpp-next.com/archive/2009/08/want-speed-pass-by-value

    – Salbei

    26. Dezember 2012 um 19:29 Uhr

1647208814 960 Mehrere Werte aus einer C Funktion zuruckgeben
Gemeinschaft

Mit C++17 können Sie auch einen oder mehrere nicht verschiebbare/nicht kopierbare Werte zurückgeben (in bestimmten Fällen). Die Möglichkeit, unbewegliche Typen zurückzugeben, kommt über die neue garantierte Rückgabewertoptimierung, und es komponiert schön mit Aggregateund was aufgerufen werden kann Template-Konstruktoren.

template<typename T1,typename T2,typename T3>
struct many {
  T1 a;
  T2 b;
  T3 c;
};

// guide:
template<class T1, class T2, class T3>
many(T1, T2, T3) -> many<T1, T2, T3>;

auto f(){ return many{string(),5.7, unmovable()}; }; 

int main(){
   // in place construct x,y,z with a string, 5.7 and unmovable.
   auto [x,y,z] = f();
}

Das Schöne daran ist, dass es garantiert nicht verursacht irgendein kopieren oder verschieben. Sie können das Beispiel machen many struct variadic auch. Mehr Details:

Rückgabe variadischer Aggregate (Struct) und Syntax für die variadische C++17-Vorlage „Construction Deduktion Guide“

999130cookie-checkMehrere Werte aus einer C++-Funktion zurückgeben

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy