Retourner plusieurs valeurs d'une fonction c++

y a-t-il un moyen privilégié de retourner des valeurs multiples à 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 souvent est d'utiliser des paramètres de référence:

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

une variation est de retourner une valeur et de passer l'autre à travers 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

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?

Edit: dans le code du monde réel, il peut y avoir plus de deux résultats. Ils peuvent aussi être de différents types.

183
c++
demandé sur Fred Larson 2008-11-26 18:19:01

18 réponses

pour retourner deux valeurs, j'utilise un std::pair (habituellement dactylographié). Vous devriez regarder boost::tuple (en C++11 et plus récent, il y a std::tuple ) pour plus de deux résultats de retour.

avec l'introduction de la liaison structurée en C++ 17, le retour de std::tuple devrait probablement devenir la norme acceptée.

158
répondu Rob 2018-03-26 19:06:34

personnellement, je n'aime généralement pas les paramètres de retour pour un certain nombre de raisons:

  • il n'est pas toujours évident dans l'invocation quels paramètres sont ins et quels sont outs
  • vous devez généralement créer une variable locale pour attraper le résultat, tandis que les valeurs de retour peuvent être utilisées en ligne (qui peut être ou non une bonne idée, mais au moins vous avez l'option)
  • il me semble plus propre d'avoir une "porte d'entrée" et un "porte de sortie" à une fonction, toutes les entrées aller dans ici, toutes les sorties à venir là-bas
  • je tiens à garder mes listes d'arguments aussi courte que possible

j'ai également quelques réserves au sujet de la technique paire/tuple. Principalement, il n'y a souvent pas d'ordre naturel pour les valeurs de retour. Comment est le lecteur du code pour savoir si résultat.d'abord le quotient ou le reste? Et l'opérateur peut modifier l'ordre, qui briserait existant code. Ceci est particulièrement insidieux si les valeurs sont du même type de sorte qu'aucune erreur de compilation ou avertissement ne serait généré. En fait, ces arguments s'appliquent aux paramètres de retour.

voici un autre exemple de code, celui-ci un peu moins 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;

est-ce que cela indique la vitesse-sol et la route, ou la route et la vitesse-sol? Il n'est pas évident.

comparez à ceci:

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;

je pense c'est plus clair.

donc je pense que mon premier choix en général est la technique struct. L'idée de pair/tuple est probablement une excellente solution dans certains cas. J'aimerais éviter les paramètres de retour quand c'est possible.

110
répondu Fred Larson 2008-11-26 17:33:07

en C++11 Vous pouvez:

#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;
}

En 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;
}

ou avec des structures:

#include <tuple>

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;
}
110
répondu pepper_chico 2017-12-16 17:01:59
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 est essentiellement votre solution struct, mais déjà définie pour vous, et prête à s'adapter à deux types de données.

22
répondu James Curran 2008-11-26 15:22:17

cela dépend entièrement de la fonction réelle et de la signification des valeurs multiples, et de leurs tailles:

  • S'ils sont liés comme dans votre exemple de fraction, alors je choisirais une instance de struct ou de classe.
  • si elles ne sont pas vraiment liées et ne peuvent pas être regroupées dans une classe / structure alors peut-être que vous devriez reformuler votre méthode en deux.
  • dépend de la taille en mémoire des valeurs que vous retournez, vous pouvez vouloir retourner un pointeur à une instance de classe ou struct, ou utiliser des paramètres de référence.
15
répondu Stewart Johnson 2008-11-26 15:21:40

la solution OO pour cela est de créer une classe de rapport. Il ne prendrait pas de code supplémentaire (en économiserait quelques-uns), serait beaucoup plus propre/plus clair, et vous donnerait quelques retouches supplémentaires vous permettant de nettoyer le code en dehors de cette classe aussi bien.

en fait, je pense que quelqu'un a recommandé de retourner une structure, qui est assez proche mais cache l'intention que ce doit être une classe pleinement pensée - out avec le constructeur et quelques méthodes, en fait, la" méthode " que vous avez mentionné à l'origine (comme retournant la paire) devrait très probablement être un membre de cette classe retournant 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 fasse beaucoup plus que n'importe quelle fonction devrait faire, si vous voulez qu'elle retourne des valeurs multiples, vous êtes presque certainement manquer un objet.

ne craignez pas de créer ces classes minuscules pour faire de petits morceaux de travail--c'est la magie de Vous finissez par le décomposer jusqu'à ce que chaque méthode soit très petite et simple et chaque classe petite et compréhensible.

autre chose qui aurait dû être un indicateur que quelque chose ne va pas: dans OO vous n'avez essentiellement pas de données--OO n'est pas sur la transmission de données, une classe doit gérer et manipuler ses propres données en interne, toute transmission de données (y compris les accesseurs) est un signe que vous pourriez avoir besoin de repenser quelque chose..

12
répondu Bill K 2008-11-26 17:45:34

avec C++17, Vous pouvez aussi retourner une ou plusieurs valeurs (dans certains cas). La possibilité de retourner les types non amovibles vient via la nouvelle optimisation de la valeur de retour garantie, et il compose bien avec agrégats , et ce qui peut être appelé constructeurs tempérés .

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();
}

la chose la plus jolie à ce sujet est qu'il est garanti de ne pas causer copier ou déplacer. Vous pouvez faire l'exemple many struct variadic aussi. Plus de détails:

Retour variadic agrégats (struct) et de la syntaxe de C++17 variadic template 'la construction de la déduction" du " guide

10
répondu Johan Lundberg 2017-05-23 11:47:06

il existe un précédent pour les structures retournantes dans la norme C (et donc C++) avec les fonctions div , ldiv (et, en C99, lldiv ) de <stdlib.h> (ou <cstdlib> ).

le "mélange de la valeur de retour et des paramètres de retour" est généralement le moins propre.

avoir une fonction retourner un état et retourner des données via les paramètres de retour est raisonnable en C; il est moins évidemment raisonnable en C++ où vous pourriez utiliser des exceptions pour relay failure information à la place.

S'il y a plus de deux valeurs de retour, alors un mécanisme de type structure est probablement préférable.

9
répondu Jonathan Leffler 2008-11-26 17:39:31

Utiliser une structure ou une classe pour la valeur de retour. Utiliser std::pair peut fonctionner pour l'instant, mais

  1. c'est inflexible si vous décidez plus tard que vous voulez plus d'info retournés;
  2. il n'est pas très clair de la déclaration de la fonction dans l'en-tête ce qui est retourné et dans quel ordre.

retourner une structure avec des noms de variables de membre auto-documentant sera probablement moins sujet aux bogues pour quiconque utilise votre fonction. En mettant mon chapeau de collègue pendant un moment, votre structure divide_result est facile pour moi, un utilisateur potentiel de votre fonction, de comprendre immédiatement après 2 secondes. Jouer avec les paramètres ouput ou des paires et tuples mystérieux prendrait plus de temps à lire et peut être utilisé incorrectement. Et très probablement même après avoir utilisé la fonction quelques fois je ne me souviendrai toujours pas de l'ordre correct des arguments.

5
répondu Michel 2017-07-24 17:08:05

si votre fonction renvoie une valeur par référence, le compilateur ne peut pas la stocker dans un registre lors de l'appel d'autres fonctions parce que, théoriquement, la première fonction peut enregistrer l'adresse de la variable qui lui a été transmise dans une variable globalement accessible, et toutes les fonctions appelées ultérieurement peuvent La changer, de sorte que le compilateur aura (1) sauver la valeur des registres de nouveau à la mémoire avant d'appeler d'autres fonctions et (2) le relire quand il est nécessaire de la mémoire à nouveau après l'un de ces appeler.

si vous revenez par référence, l'optimisation de votre programme en souffrira""

4
répondu dmityugov 2008-11-26 16:00:42

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). le programme est comme une calculatrice.

#  include <tuple>
# include <iostream>

using namespace std; 

tuple < int,int,int,int,int >   cal(int n1, int n2)
{
    return  make_tuple(n1/n2,n1%n2,n1+n2,n1-n2,n1*n2);
}

int main()
{
    int qut,rer,add,sub,mul,a,b;
    cin>>a>>b;
    tie(qut,rer,add,sub,mul)=cal(a,b);
    cout << "quotient= "<<qut<<endl;
    cout << "remainder= "<<rer<<endl;
    cout << "addition= "<<add<<endl;
    cout << "subtraction= "<<sub<<endl;
    cout << "multiplication= "<<mul<<endl;
    return 0;
}

ainsi, vous pouvez clairement comprendre que de cette façon vous pouvez retourner plusieurs valeurs d'une fonction. en utilisant std:: pair seulement 2 valeurs peuvent être retournées tandis que std::tuple peut retourner plus de deux valeurs.

4
répondu PRAFUL ANAND 2017-01-23 08:34:45

j'ai tendance à utiliser des valeurs externes dans des fonctions comme celle-ci, parce que je m'en tiens au paradigme d'une fonction qui renvoie des codes de réussite/d'erreur et j'aime garder les choses uniformes.

3
répondu John Dibling 2008-11-26 15:22:32
Les variantes

comprennent les matrices, générateurs et inversion de commande , mais aucune n'est appropriée ici.

certains (par exemple Microsoft dans Win32 historique) ont tendance à utiliser des paramètres de référence pour la simplicité, parce qu'il est clair qui attribue et comment il regardera sur la pile, réduit la prolifération des structures, et permet une valeur de retour séparée pour le succès.

"Pure" programmeurs préfèrent le struct, en supposant qu'il est la valeur de la fonction (comme c'est le cas ici), plutôt que quelque chose qui est touché incidemment par la fonction. Si vous aviez une procédure plus compliquée, ou quelque chose avec l'état, vous utiliseriez probablement des références (en supposant que vous avez une raison de ne pas utiliser une classe).

2
répondu Mark 2008-11-26 15:39:02

je dirais qu'il n'existe 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 doivent être utilisés ensemble dans un traitement ultérieur, alors les structures ont du sens, sinon j'aurais tendance à passer alors en tant que références individuelles à moins que la fonction allait être utilisée dans un énoncé composite:

x = divide( x, y, z ) + divide( a, b, c );

je choisis souvent de passer 'structures out' par référence dans la liste des paramètres plutôt que d'avoir le passer par la copie au-dessus de retour d'une nouvelle structure (mais c'est la transpiration de la petite substance).

void divide(int dividend, int divisor, Answer &ans)

est-ce que les paramètres sont confus? Un paramètre envoyé comme référence suggère la valeur va changer (par opposition à un const référence). La dénomination judicieuse élimine également la confusion.

2
répondu Patrick 2008-11-26 15:40:18

plutôt que de retourner plusieurs valeurs,il suffit de retourner l'une d'elles et de faire une référence d'autres dans la fonction requise par exemple:

int divide(int a,int b,int quo,int &rem)
2
répondu Anchit Rana 2017-11-16 18:58:02

Pourquoi insistez-vous sur une fonction avec plusieurs valeurs de retour? Avec OOP 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 données supplémentaires des membres, mais n'est pas requis pour ce faire. Il s'agit de la méthode préférée pour les appels compliqués de base de données ou de réseau, où beaucoup d'informations de retour supplémentaires peuvent être nécessaires en cas d'erreurs se produisent.

pour répondre à votre question initiale, cet exemple a une méthode pour retourner le quotient, qui est ce que 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.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};
1
répondu Roland 2014-11-21 10:28:07

Boost tuple serait mon choix préféré pour un système généralisé de retourner plus d'une valeur d'une fonction.

exemple Possible:

include "boost/tuple/tuple.hpp"

tuple <int,int> divide( int dividend,int divisor ) 

{
  return make_tuple(dividend / divisor,dividend % divisor )
}
1
répondu AndyUK 2015-02-04 11:26:41

nous pouvons déclarer la fonction telle que, elle renvoie une variable définie par l'utilisateur de type de structure ou un pointeur vers elle . Et par la propriété d'une structure, nous savons qu'une structure en C peut contenir des valeurs multiples de types asymétriques (i.e. une variable int, quatre variables char, deux variables float et ainsi de suite...)

1
répondu Rohit Hajare 2015-10-20 11:02:58