Gibt es eine bevorzugte Möglichkeit, mehrere Werte von einer C ++ - Funktion zurückzugeben? Stellen Sie sich zum Beispiel eine Funktion vor, die zwei ganze Zahlen teilt 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 über 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 eine dieser Möglichkeiten allgemein bevorzugt oder gibt es andere Vorschläge?
Bearbeiten: Im realen Code kann es mehr als zwei Ergebnisse geben. Sie können auch von verschiedenen Typen sein.
std::tuple
.std::tie
stackoverflow.com/a/2573822/502144In C ++ 11 können Sie:
In C ++ 17:
oder mit Strukturen:
quelle
struct
Zeile außerhalb des Funktionskörpers und ersetzen Sie dieauto
Funktionsrückgabe durchresult
.divide
in eine separate CPP-Datei einfügen möchten ? Ich bekomme den Fehlererror: use of ‘auto divide(int, int)’ before deduction of ‘auto’
. Wie löse ich das?Persönlich mag ich Rückgabeparameter aus einer Reihe von Gründen im Allgemeinen nicht:
Ich habe auch einige Vorbehalte gegen die Paar / Tupel-Technik. Hauptsächlich gibt es oft keine natürliche Reihenfolge für die Rückgabewerte. Wie kann der Leser des Codes wissen, ob result.first der Quotient oder der Rest ist? Und der Implementierer könnte die Reihenfolge ändern, wodurch der vorhandene Code beschädigt würde. Dies ist besonders heimtückisch, wenn die Werte vom gleichen Typ sind, sodass kein Compilerfehler oder keine Warnung generiert wird. Tatsächlich gelten diese Argumente auch für Rückgabeparameter.
Hier ist ein weiteres Codebeispiel, das etwas weniger trivial ist:
Gibt dies Grundgeschwindigkeit und Kurs oder Kurs und Grundgeschwindigkeit aus? Es ist nicht offensichtlich.
Vergleichen Sie damit:
Ich denke das ist klarer.
Daher denke ich, dass meine erste Wahl im Allgemeinen die Strukturtechnik ist. Die Paar / Tupel-Idee ist in bestimmten Fällen wahrscheinlich eine großartige Lösung. Ich möchte die Rückgabeparameter nach Möglichkeit vermeiden.
quelle
struct
Like zu deklarieren,Velocity
ist nett. Eine Sorge ist jedoch, dass es den Namespace verschmutzt. Ich nehme an, dass mit C ++ 11 diestruct
einen langen Typnamen haben können und man verwenden kannauto result = calculateResultingVelocity(...)
.struct { int a, b; } my_func();
. Dies könnte wie folgt verwendet werden :auto result = my_func();
. C ++ erlaubt dies jedoch nicht: "Neue Typen dürfen in einem Rückgabetyp nicht definiert werden". Also muss ich Strukturen erstellen wiestruct my_func_result_t
...auto
,auto result = my_func();
ist also trivial erhältlich.std :: pair ist im Wesentlichen Ihre Strukturlösung, aber bereits für Sie definiert und bereit, sich an zwei beliebige Datentypen anzupassen.
quelle
Es hängt ganz von der tatsächlichen Funktion und der Bedeutung der mehreren Werte und ihrer Größe ab:
quelle
Die OO-Lösung hierfür besteht darin, eine Verhältnisklasse zu erstellen. Es würde keinen zusätzlichen Code erfordern (würde etwas sparen), wäre wesentlich 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 nah genug ist, aber die Absicht verbirgt, dass dies eine vollständig durchdachte Klasse mit Konstruktor und einigen Methoden sein muss, in der Tat die "Methode", die Sie ursprünglich erwähnt haben (als Rückgabe der Paar) sollte höchstwahrscheinlich ein Mitglied dieser Klasse sein, das eine Instanz von sich selbst zurückgibt.
Ich weiß, dass Ihr Beispiel nur ein "Beispiel" war, aber Tatsache ist, dass Sie mit ziemlicher Sicherheit ein Objekt vermissen, wenn Ihre Funktion nicht mehr als jede andere Funktion ausführen soll, wenn Sie möchten, dass sie mehrere Werte zurückgibt.
Haben Sie keine Angst, diese winzigen Klassen zu erstellen, um kleine Arbeiten zu erledigen - das ist die Magie von OO - Sie brechen sie am Ende auf, bis jede Methode sehr klein und einfach und jede Klasse klein und verständlich ist.
Eine andere Sache, die ein Indikator dafür sein sollte, 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 bearbeiten, alle Daten, die weitergegeben werden (einschließlich Accessoren). ist ein Zeichen dafür, dass Sie möglicherweise etwas überdenken müssen ..
quelle
Es ist Präzedenzfall für Strukturen in der C (und damit die C ++) -Standard mit der zurückkehrende
div
,ldiv
(und in C99,lldiv
) Funktionen aus<stdlib.h>
(oder<cstdlib>
).Die 'Mischung aus Rückgabewert und Rückgabeparametern' ist normalerweise am wenigsten sauber.
Es ist in C sinnvoll, wenn eine Funktion einen Status zurückgibt und Daten über Rückgabeparameter zurückgibt. In C ++ ist dies weniger sinnvoll, da Sie stattdessen Ausnahmen verwenden können, um Fehlerinformationen weiterzuleiten.
Wenn es mehr als zwei Rückgabewerte gibt, ist ein strukturähnlicher Mechanismus wahrscheinlich am besten.
quelle
Mit C ++ 17 können Sie auch einen oder mehrere nicht verschiebbare / nicht kopierbare Werte zurückgeben (in bestimmten Fällen). Die Möglichkeit, nicht verschiebbare Typen zurückzugeben, ergibt sich aus der neuen Optimierung des garantierten Rückgabewerts, die sich gut aus Aggregaten und so genannten Vorlagenkonstruktoren zusammensetzt .
Das Schöne daran ist, dass es garantiert kein Kopieren oder Verschieben verursacht. Sie können die Beispielstruktur auch
many
variadic machen. Mehr Details:Rückgabe von variadischen Aggregaten (struct) und Syntax für die varadische C ++ 17-Vorlage 'Konstruktionsabzugshandbuch'
quelle
Es gibt verschiedene Möglichkeiten, mehrere Parameter zurückzugeben. Ich werde exhastiv sein.
Referenzparameter verwenden:
Zeigerparameter verwenden:
Dies hat den Vorteil, dass Sie
&
an der Anrufstelle eine Aktion durchführen müssen, um möglicherweise die Leute darauf aufmerksam zu machen, dass es sich um einen Out-Parameter handelt.Schreiben Sie eine Vorlage und verwenden Sie sie:
dann können wir tun:
und alles ist gut.
foo
kann keinen als Bonus übergebenen Wert mehr lesen.Zum Erstellen können Sie auch andere Möglichkeiten zum Definieren eines Spots verwenden, an dem Sie Daten ablegen können
out
. Ein Rückruf zum Beispiel, um Dinge irgendwo zu platzieren.Wir können eine Struktur zurückgeben:
whick funktioniert in jeder Version von C ++ und in c ++ 17 dies erlaubt auch:
zu null Kosten. Parameter können dank garantierter Elision nicht einmal verschoben werden.
Wir könnten ein
std::tuple
:Das hat den Nachteil, dass Parameter nicht benannt werden. Dies ermöglicht diec ++ 17::
auch. Vorc ++ 17 wir können stattdessen tun:
das ist nur ein bisschen umständlicher. Garantierte Elision funktioniert hier jedoch nicht.
Wenn
out<>
wir fremdes Gebiet betreten (und das ist danach !), Können wir den Continuation-Passing-Stil verwenden:und jetzt tun Anrufer:
Ein Vorteil dieses Stils besteht darin, dass Sie eine beliebige Anzahl von Werten (mit einheitlichem Typ) zurückgeben können, ohne den Speicher verwalten zu müssen:
Der
value
Rückruf kann bei Ihnen 500 Mal aufgerufen werdenget_all_values( [&](int value){} )
.Für reinen Wahnsinn könnten Sie sogar eine Fortsetzung der Fortsetzung verwenden.
deren Verwendung sieht aus wie:
das würde viele-eins-Beziehungen zwischen
result
und ermöglichenother
.Wieder mit unifornischen Werten können wir dies tun:
Hier nennen wir den Rückruf mit einer Reihe von Ergebnissen. Wir können dies sogar wiederholt tun.
Mit dieser Funktion können Sie eine Funktion verwenden, die Megabyte an Daten effizient weiterleitet, ohne eine Zuordnung vom Stapel vorzunehmen.
std::function
Dies ist jetzt etwas schwierig, da wir dies in Umgebungen ohne Zuweisung ohne Overhead tun würden. Wir möchten also einefunction_view
, die niemals zuweist.Eine andere Lösung ist:
Anstatt den Rückruf anzunehmen und aufzurufen, wird
foo
stattdessen eine Funktion zurückgegeben, die den Rückruf entgegennimmt.foo (7) ([&] (int result, int other_result) {/ * code * /}); Dadurch werden die Ausgabeparameter von den Eingabeparametern getrennt, indem separate Klammern verwendet werden.
Mit
variant
undc ++ 20Coroutinen, Sie könntenfoo
einen Generator aus einer Variante der Rückgabetypen (oder nur dem Rückgabetyp) erstellen. Die Syntax ist noch nicht festgelegt, daher werde ich keine Beispiele nennen.In der Welt der Signale und Slots eine Funktion, die eine Reihe von Signalen verfügbar macht:
Mit
foo
dieser Option können Sie eine erstellen , die asynchron funktioniert und das Ergebnis nach Abschluss sendet.In dieser Richtung gibt es eine Vielzahl von Pipeline-Techniken, bei denen eine Funktion nichts tut, sondern dafür sorgt, dass Daten auf irgendeine Weise verbunden werden, und das Vorgehen ist relativ unabhängig.
dann ist dieser Code nicht tut nichts , bis
int_source
ganze Zahlen , es zu bieten hat. Wenn es der Fall ist,int_dest1
undint_dest2
starten Sie die Ergebnisse recieving.quelle
auto&&[result, other_result]=foo();
Funktionen, die sowohl Tupel als auch Strukturen zurückgeben. Vielen Dank!Verwenden Sie eine Struktur oder eine Klasse für den Rückgabewert. Die Verwendung
std::pair
mag vorerst funktionieren, aberDas Zurückgeben einer Struktur mit selbstdokumentierenden Mitgliedsvariablennamen ist wahrscheinlich weniger fehleranfällig für alle, die Ihre Funktion verwenden. Wenn Sie für einen Moment meinen Kollegenhut aufsetzen, ist Ihre
divide_result
Struktur für mich, einen potenziellen Benutzer Ihrer Funktion, nach 2 Sekunden sofort verständlich. Das Herumspielen mit Ausgabeparametern oder mysteriösen Paaren und Tupeln würde mehr Zeit zum Lesen benötigen und möglicherweise falsch verwendet werden. Und höchstwahrscheinlich kann ich mich auch nach mehrmaliger Verwendung der Funktion nicht an die richtige Reihenfolge der Argumente erinnern.quelle
Wenn Ihre Funktion einen Wert über eine Referenz zurückgibt, kann der Compiler ihn beim Aufrufen anderer Funktionen nicht in einem Register speichern, da die erste Funktion theoretisch die Adresse der ihr übergebenen Variablen in einer global zugänglichen Variablen speichern kann und alle nachfolgend aufgerufenen Funktionen dies können Ändern Sie es, damit der Compiler (1) den Wert aus den Registern zurück in den Speicher speichern muss, bevor er andere Funktionen aufruft, und (2) ihn erneut liest, wenn er nach einem solchen Aufruf erneut aus dem Speicher benötigt wird.
Wenn Sie als Referenz zurückkehren, leidet die Optimierung Ihres Programms
quelle
Hier schreibe ich ein Programm, das mehrere Werte (mehr als zwei Werte) in c ++ zurückgibt. Dieses Programm ist in c ++ 14 (G ++ 4.9.2) ausführbar. Programm ist wie ein Taschenrechner.
Sie können also klar verstehen, dass Sie auf diese Weise mehrere Werte von einer Funktion zurückgeben können. Mit std :: pair können nur 2 Werte zurückgegeben werden, während std :: tuple mehr als zwei Werte zurückgeben kann.
quelle
auto
Rückgabetyp aktivierencal
, um dies noch sauberer zu machen. (IMO).Ich neige dazu, Out-Vals in Funktionen wie diesen zu verwenden, weil ich mich an das Paradigma einer Funktion halte, die Erfolgs- / Fehlercodes zurückgibt, und ich mag es, die Dinge einheitlich zu halten.
quelle
Alternativen umfassen Arrays, Generatoren und Inversion der Steuerung , aber keine ist hier angemessen.
Einige (z. B. Microsoft im historischen Win32) verwenden der Einfachheit halber Referenzparameter, da klar ist, wer auf dem Stapel zugeordnet ist und wie es aussehen wird, die Verbreitung von Strukturen verringert und einen separaten Rückgabewert für den Erfolg ermöglicht.
"Reine" Programmierer bevorzugen die Struktur, vorausgesetzt, es ist der Funktionswert (wie hier der Fall), anstatt etwas, das zufällig von der Funktion berührt wird. Wenn Sie eine kompliziertere Prozedur oder etwas mit Status hätten, würden Sie wahrscheinlich Referenzen verwenden (vorausgesetzt, Sie haben einen Grund, keine Klasse zu verwenden).
quelle
Ich würde sagen, es gibt keine bevorzugte Methode, alles hängt davon ab, was Sie mit der Antwort machen werden. Wenn die Ergebnisse bei der weiteren Verarbeitung zusammen verwendet werden sollen, sind Strukturen sinnvoll. Wenn nicht, würde ich sie eher als einzelne Referenzen übergeben, es sei denn, die Funktion würde in einer zusammengesetzten Anweisung verwendet:
x = divide( x, y, z ) + divide( a, b, c );
Ich entscheide mich oft dafür, 'Strukturen' als Referenz in der Parameterliste auszugeben, anstatt den Aufwand für das Übergeben einer Kopie durch das Zurückgeben einer neuen Struktur zu haben (aber das schwitzt die kleinen Dinge).
void divide(int dividend, int divisor, Answer &ans)
Sind unsere Parameter verwirrend? Ein als Referenz gesendeter Parameter deutet darauf hin, dass sich der Wert ändern wird (im Gegensatz zu einer konstanten Referenz). Eine vernünftige Benennung beseitigt auch Verwirrung.
quelle
Warum bestehen Sie auf einer Funktion mit mehreren Rückgabewerten? Mit OOP können Sie eine Klasse verwenden, die eine reguläre Funktion mit einem einzelnen Rückgabewert und einer beliebigen Anzahl zusätzlicher "Rückgabewerte" wie unten bietet. Der Vorteil besteht darin, dass der Anrufer die Möglichkeit hat, sich die zusätzlichen Datenelemente anzusehen, dies jedoch nicht tun muss. Dies ist die bevorzugte Methode für komplizierte Datenbank- oder Netzwerkanrufe, bei denen im Fehlerfall viele zusätzliche Rückgabeinformationen erforderlich sein können.
Um Ihre ursprüngliche Frage zu beantworten, enthält dieses Beispiel eine Methode zum Zurückgeben des Quotienten, die die meisten Anrufer möglicherweise benötigen. Außerdem können Sie nach dem Methodenaufruf den Rest als Datenelement abrufen.
quelle
Anstatt mehrere Werte zurückzugeben, geben Sie einfach einen von ihnen zurück und verweisen Sie in der erforderlichen Funktion auf andere, z.
quelle
Boost-Tupel wäre meine bevorzugte Wahl für ein verallgemeinertes System, bei dem mehr als ein Wert von einer Funktion zurückgegeben wird.
Mögliches Beispiel:
quelle
Wir können die Funktion so deklarieren, dass sie eine benutzerdefinierte Variable vom Strukturtyp oder einen Zeiger darauf zurückgibt. Und durch die Eigenschaft einer Struktur wissen wir, dass eine Struktur in C mehrere Werte asymmetrischer Typen enthalten kann (dh eine int-Variable, vier char-Variablen, zwei float-Variablen usw.)
quelle
Ich würde es nur als Referenz tun, wenn es nur ein paar Rückgabewerte sind, aber für komplexere Typen können Sie es auch einfach so machen:
Verwenden Sie "static", um den Umfang des Rückgabetyps auf diese Kompilierungseinheit zu beschränken, wenn es sich nur um einen temporären Rückgabetyp handelt.
Dies ist definitiv nicht der schönste Weg, aber es wird funktionieren.
quelle
Hier ist ein vollständiges Beispiel für eine solche Problemlösung
quelle