Existe-t-il un moyen préféré de renvoyer plusieurs valeurs à partir d'une fonction C ++? Par exemple, imaginez une fonction qui divise deux entiers et renvoie à la fois le quotient et le reste. Une façon que je vois couramment est d'utiliser des paramètres de référence:
void divide(int dividend, int divisor, int& quotient, int& remainder);
Une variante consiste à renvoyer une valeur et à passer l'autre via un paramètre de référence:
int divide(int dividend, int divisor, int& remainder);
Une autre façon serait de déclarer une structure pour contenir tous les résultats et de retourner que:
struct divide_result {
int quotient;
int remainder;
};
divide_result divide(int dividend, int divisor);
Est-ce que l'une de ces façons est généralement préférée ou y a-t-il d'autres suggestions?
Modifier: dans le code du monde réel, il peut y avoir plus de deux résultats. Ils peuvent également être de différents types.
std::tuple
.std::tie
stackoverflow.com/a/2573822/502144En C ++ 11, vous pouvez:
En C ++ 17:
ou avec des structures:
la source
struct
ligne à l'extérieur du corps de la fonction et de remplacer laauto
fonction return parresult
.divide
dans un fichier cpp séparé? Je reçois l'erreurerror: use of ‘auto divide(int, int)’ before deduction of ‘auto’
. Comment résoudre ce problème?Personnellement, je n'aime généralement pas les paramètres de retour pour plusieurs raisons:
J'ai également quelques réserves sur la technique paire / tuple. Surtout, il n'y a souvent pas d'ordre naturel pour les valeurs de retour. Comment le lecteur du code peut-il savoir si result.first est le quotient ou le reste? Et l'implémenteur pourrait changer l'ordre, ce qui casserait le code existant. Ceci est particulièrement insidieux si les valeurs sont du même type afin qu'aucune erreur ou avertissement du compilateur ne soit généré. En fait, ces arguments s'appliquent également aux paramètres de retour.
Voici un autre exemple de code, celui-ci un peu moins trivial:
Est-ce que cela imprime la vitesse et le cap, ou le cap et la vitesse au sol? Ce n'est pas évident.
Comparez à ceci:
Je pense que c'est plus clair.
Je pense donc que mon premier choix en général est la technique de struct. L'idée paire / tuple est probablement une excellente solution dans certains cas. Je voudrais éviter les paramètres de retour lorsque cela est possible.
la source
struct
semblableVelocity
est une bonne idée. Cependant, une préoccupation est qu'elle pollue l'espace de noms. Je suppose qu'avec C ++ 11, lestruct
peut avoir un nom de type long et on peut l'utiliserauto result = calculateResultingVelocity(...)
.struct { int a, b; } my_func();
. Cela pourrait être utilisé comme ceci:auto result = my_func();
. Mais C ++ ne le permet pas: "les nouveaux types ne peuvent pas être définis dans un type de retour". Je dois donc créer des structures commestruct my_func_result_t
...auto
,auto result = my_func();
est donc facilement accessible.std :: pair est essentiellement votre solution de struct, mais déjà définie pour vous, et prête à s'adapter à deux types de données.
la source
Cela dépend entièrement de la fonction réelle et de la signification des valeurs multiples, et de leurs tailles:
la source
La solution OO pour cela consiste à créer une classe de rapport. Cela ne prendrait pas de code supplémentaire (en économiserait), serait beaucoup plus propre / plus clair et vous donnerait des remaniements supplémentaires vous permettant de nettoyer le code en dehors de cette classe.
En fait, je pense que quelqu'un a recommandé de retourner une structure, qui est assez proche mais cache l'intention que cela doit être une classe bien pensée avec un constructeur et quelques méthodes, en fait, la "méthode" que vous avez mentionnée à l'origine (comme renvoyant le paire) devrait très probablement être un membre de cette classe renvoyant une instance de lui-même.
Je sais que votre exemple n'était qu'un "exemple", mais le fait est qu'à moins que votre fonction ne fasse beaucoup plus que n'importe quelle fonction, si vous voulez qu'elle renvoie plusieurs valeurs, il vous manque presque certainement un objet.
N'ayez pas peur de créer ces petites classes pour faire de petits travaux - c'est la magie de OO - vous finissez par la décomposer jusqu'à ce que chaque méthode soit très petite et simple et chaque classe petite et compréhensible.
Une autre chose qui aurait dû être un indicateur que quelque chose n'allait pas: dans OO, vous n'avez pratiquement pas de données - OO ne consiste pas à transmettre des données, une classe doit gérer et manipuler ses propres données en interne, toutes les données passant (y compris les accesseurs) est un signe que vous devrez peut-être repenser quelque chose ..
la source
Il existe un précédent pour renvoyer des structures dans le standard C (et donc C ++) avec les fonctions
div
,ldiv
(et, en C99lldiv
) de<stdlib.h>
(ou<cstdlib>
).Le «mélange de valeur de retour et de paramètres de retour» est généralement le moins net.
Avoir une fonction renvoyer un état et renvoyer des données via des paramètres de retour est judicieux en C; il est moins évident en C ++ où vous pouvez utiliser des exceptions pour relayer les informations d'échec à la place.
S'il y a plus de deux valeurs de retour, alors un mécanisme de type structure est probablement le meilleur.
la source
Avec C ++ 17, vous pouvez également renvoyer une ou plusieurs valeurs inamovibles / non copiables (dans certains cas). La possibilité de renvoyer des types inamovibles provient de la nouvelle optimisation de la valeur de retour garantie, et elle se compose parfaitement avec les agrégats et ce que l'on peut appeler des constructeurs basés sur des modèles .
La jolie chose à ce sujet est qu'il est garanti de ne pas provoquer de copie ou de déplacement. Vous pouvez également rendre l'exemple
many
struct struct variadic. Plus de détails:Retour des agrégats variadiques (struct) et de la syntaxe pour le modèle variadique C ++ 17 'guide de déduction de construction'
la source
Il existe de nombreuses façons de renvoyer plusieurs paramètres. Je vais être exagéré.
Utilisez des paramètres de référence:
utiliser les paramètres du pointeur:
ce qui a l'avantage que vous devez faire
&
sur le site d'appel, alertant peut-être les gens que c'est un paramètre externe.Écrivez un modèle et utilisez-le:
alors nous pouvons faire:
et tout va bien.
foo
n'est plus en mesure de lire les valeurs transmises en bonus.D'autres façons de définir un emplacement où vous pouvez placer des données peuvent être utilisées pour construire
out
. Un rappel pour mettre des choses quelque part, par exemple.Nous pouvons renvoyer une structure:
whick fonctionne bien dans chaque version de C ++, et c ++ 17 cela permet également:
à coût nul. Les paramètres ne peuvent même pas être déplacés grâce à l'élision garantie.
Nous pourrions retourner un
std::tuple
:qui a l'inconvénient que les paramètres ne sont pas nommés. Cela permet auc ++ 17:
ainsi que. Antérieur àc ++ 17 on peut plutôt faire:
ce qui est un peu plus gênant. L'élision garantie ne fonctionne cependant pas ici.
En entrant dans un territoire étranger (et c'est après
out<>
!), Nous pouvons utiliser le style de passage de continuation:et maintenant les appelants font:
un avantage de ce style est que vous pouvez retourner un nombre arbitraire de valeurs (avec un type uniforme) sans avoir à gérer la mémoire:
le
value
rappel pourrait être appelé 500 fois lorsque vousget_all_values( [&](int value){} )
.Pour la folie pure, vous pouvez même utiliser une suite sur la suite.
dont l'utilisation ressemble à:
qui permettrait des relations multiples entre
result
etother
.Encore une fois avec des valeurs uniforn, nous pouvons le faire:
ici, nous appelons le rappel avec une gamme de résultats. Nous pouvons même le faire à plusieurs reprises.
En utilisant cela, vous pouvez avoir une fonction qui transmet efficacement les mégaoctets de données sans faire d'allocation hors de la pile.
Maintenant,
std::function
c'est un peu lourd pour cela, car nous le ferions dans des environnements sans allocation sans frais généraux. Nous voudrions donc unfunction_view
qui n'alloue jamais.Une autre solution est:
où au lieu de prendre le rappel et de l'invoquer,
foo
renvoie à la place une fonction qui prend le rappel.foo (7) ([&] (int result, int other_result) {/ * code * /}); cela rompt les paramètres de sortie des paramètres d'entrée en ayant des crochets séparés.
Avec
variant
etc ++ 20coroutines, vous pouvez créerfoo
un générateur d'une variante des types de retour (ou simplement du type de retour). La syntaxe n'est pas encore fixée, donc je ne donnerai pas d'exemples.Dans le monde des signaux et des slots, une fonction qui expose un ensemble de signaux:
vous permet de créer un
foo
qui fonctionne asynchrone et diffuse le résultat une fois terminé.En bas de cette ligne, nous avons une variété de techniques de pipeline, où une fonction ne fait pas quelque chose mais organise plutôt la connexion des données d'une certaine manière, et le faire est relativement indépendant.
alors ce code ne fait rien tant qu'il
int_source
n'a pas d'entiers pour le fournir. Quand c'est le cas,int_dest1
etint_dest2
commencez à recevoir les résultats.la source
auto&&[result, other_result]=foo();
fonctions renvoyant à la fois des tuples et des structures. Merci!Utilisez une structure ou une classe pour la valeur de retour. L'utilisation
std::pair
peut fonctionner pour l'instant, maisLe retour d'une structure avec des noms de variables membres auto-documentés sera probablement moins sujet aux bogues pour quiconque utilise votre fonction. En mettant mon chapeau de collègue pendant un moment, votre
divide_result
structure est facile pour moi, un utilisateur potentiel de votre fonction, à comprendre immédiatement après 2 secondes. Jouer avec des paramètres de sortie ou des paires et des tuples mystérieux prendrait plus de temps à lire et pourrait être utilisé de manière incorrecte. Et très probablement même après avoir utilisé la fonction plusieurs fois, je ne me souviens toujours pas de l'ordre correct des arguments.la source
Si votre fonction renvoie une valeur via référence, le compilateur ne peut pas la stocker dans un registre lors de l'appel d'autres fonctions car, théoriquement, la première fonction peut enregistrer l'adresse de la variable qui lui est transmise dans une variable accessible globalement, et toutes les fonctions appelées subséquemment peuvent changez-le, ainsi le compilateur devra (1) sauvegarder la valeur des registres dans la mémoire avant d'appeler d'autres fonctions et (2) la relire quand elle sera nécessaire à partir de la mémoire après l'un de ces appels.
Si vous revenez par référence, l'optimisation de votre programme en souffrira
la source
Ici, j'écris un programme qui renvoie plusieurs valeurs (plus de deux valeurs) en c ++. Ce programme est exécutable en c ++ 14 (G ++ 4.9.2). programme est comme une calculatrice.
Ainsi, vous pouvez clairement comprendre que de cette manière, vous pouvez renvoyer plusieurs valeurs à partir d'une fonction. en utilisant std :: pair, seulement 2 valeurs peuvent être retournées tandis que std :: tuple peut retourner plus de deux valeurs.
la source
auto
type de retourcal
pour rendre cela encore plus propre. (OMI).J'ai tendance à utiliser des valeurs plus élevées dans des fonctions comme celle-ci, car je m'en tiens au paradigme d'une fonction renvoyant des codes de réussite / d'erreur et j'aime garder les choses uniformes.
la source
Les alternatives incluent les tableaux, les générateurs et l' inversion de contrôle , mais aucun n'est approprié ici.
Certains (par exemple, Microsoft dans Win32 historique) ont tendance à utiliser des paramètres de référence pour plus de simplicité, car il est clair qui alloue et à quoi il ressemblera sur la pile, réduit la prolifération des structures et permet une valeur de retour distincte pour le succès.
Programmeurs « Pure » préfèrent la struct, en supposant qu'il est la valeur de fonction (comme cela est le cas ici), plutôt que quelque chose qui soit dit en passant touché par la fonction. Si vous aviez une procédure plus compliquée, ou quelque chose avec un état, vous utiliseriez probablement des références (en supposant que vous avez une raison de ne pas utiliser une classe).
la source
Je dirais qu'il n'y a pas de méthode préférée, tout dépend de ce que vous allez faire avec la réponse. Si les résultats vont être utilisés ensemble dans un traitement ultérieur, les structures ont du sens, sinon j'aurais tendance à passer comme références individuelles à moins que la fonction ne soit utilisée dans une instruction composite:
x = divide( x, y, z ) + divide( a, b, c );
Je choisis souvent de passer les «structures» par référence dans la liste des paramètres plutôt que de faire passer par copie les frais généraux de retour d'une nouvelle structure (mais cela transpire les petites choses).
void divide(int dividend, int divisor, Answer &ans)
Les paramètres sont-ils déroutants? Un paramètre envoyé comme référence suggère que la valeur va changer (par opposition à une référence const). Une dénomination sensible élimine également la confusion.
la source
Pourquoi insistez-vous sur une fonction avec plusieurs valeurs de retour? Avec la POO, vous pouvez utiliser une classe offrant une fonction régulière avec une seule valeur de retour, et n'importe quel nombre de "valeurs de retour" supplémentaires comme ci-dessous. L'avantage est que l'appelant a le choix de regarder les membres de données supplémentaires, mais n'est pas obligé de le faire. Il s'agit de la méthode préférée pour les appels de base de données ou de réseau complexes, où de nombreuses informations de retour supplémentaires peuvent être nécessaires en cas d'erreurs.
Pour répondre à votre question d'origine, cet exemple a une méthode pour renvoyer le quotient, ce dont la plupart des appelants peuvent avoir besoin, et en outre, après l'appel de méthode, vous pouvez obtenir le reste en tant que membre de données.
la source
plutôt que de renvoyer plusieurs valeurs, renvoyez simplement l'une d'entre elles et faites une référence à d'autres dans la fonction requise pour par exemple:
la source
Boost tuple serait mon choix préféré pour un système généralisé de retour de plus d'une valeur d'une fonction.
Exemple possible:
la source
Nous pouvons déclarer la fonction de telle sorte qu'elle renvoie une variable définie par l'utilisateur de type structure ou un pointeur sur celle-ci. Et par la propriété d'une structure, on sait qu'une structure en C peut contenir plusieurs valeurs de types asymétriques (ie une variable int, quatre variables char, deux variables float et ainsi de suite…)
la source
Je le ferais simplement par référence si ce n'est que quelques valeurs de retour, mais pour les types plus complexes, vous pouvez également le faire comme ceci:
utilisez "statique" pour limiter la portée du type de retour à cette unité de compilation si elle est uniquement destinée à être un type de retour temporaire.
Ce n'est certainement pas la plus jolie façon de le faire, mais cela fonctionnera.
la source
Voici un exemple complet de ce type de solution de problème
la source