Renvoyer plusieurs valeurs d'une fonction C ++

242

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.

Fred Larson
la source

Réponses:

217

Pour retourner deux valeurs, j'utilise un std::pair(généralement typedef'd). 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 std::tupledevrait probablement devenir une norme acceptée.

Rob
la source
12
+1 pour tuple. Gardez à l'esprit les ramifications de performances des gros objets retournant dans une structure par rapport à passant par référence.
Marcin
12
Si vous allez utiliser des tuples, pourquoi ne pas les utiliser également pour les paires. Pourquoi avoir un cas particulier?
Ferruccio
4
Fred, oui boost :: tuple peut le faire :)
Johannes Schaub - litb
46
En C ++ 11, vous pouvez utiliser std::tuple.
Ferruccio
14
Si vous souhaitez accepter plusieurs valeurs d'une fonction, un moyen pratique de le faire est d'utiliser std::tie stackoverflow.com/a/2573822/502144
fdermishin
176

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:

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;
}
pepper_chico
la source
4
J'ai une préoccupation avec les fonctions renvoyant des tuples. Supposons que le prototype de fonction ci-dessus se trouve dans un en-tête, alors comment savoir ce que signifient les première et deuxième valeurs renvoyées sans comprendre la définition de la fonction? quotient-reste ou reste-quotient.
Uchia Itachi
7
@UchiaItachi Même souci pour les paramètres de fonction, vous pouvez leur donner des noms, mais le langage ne l'impose même pas, et les noms des paramètres n'ont aucune valeur sur le site d'appel lors de la lecture. De plus, sur un seul retour, vous n'avez qu'un type, mais avoir le nom peut également être utile, avec des tuples, vous doublez simplement le problème, donc imo, le langage manque juste concernant l'auto-documentation de plusieurs façons, pas seulement cela.
pepper_chico
1
à quoi ressemblerait le dernier exemple si je voulais spécifier explicitement le type de retour de divide ()? Dois-je alors définir le résultat ailleurs, ou le définir directement dans la spécification du type de retour?
Slava
1
@Slava, vous ne pouvez pas définir un type directement à la signature de la fonction, vous devez déclarer le type à l'extérieur et l'utiliser comme type de retour, comme cela se fait normalement (il suffit de déplacer la structligne à l'extérieur du corps de la fonction et de remplacer la autofonction return par result.
pepper_chico
3
@pepper_chico Et si vous voulez mettre la définition de la fonction dividedans un fichier cpp séparé? Je reçois l'erreur error: use of ‘auto divide(int, int)’ before deduction of ‘auto’. Comment résoudre ce problème?
Adriaan
123

Personnellement, je n'aime généralement pas les paramètres de retour pour plusieurs raisons:

  • il n'est pas toujours évident dans l'invocation quels paramètres sont ins et lesquels 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 (ce qui peut ou non être une bonne idée, mais au moins vous avez l'option)
  • il me semble plus propre d'avoir une fonction "in door" et une "out door" pour une fonction - toutes les entrées entrent ici, toutes les sorties sortent là-bas
  • J'aime garder mes listes d'arguments aussi courtes que possible

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:

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 imprime la vitesse et le cap, ou le cap et la vitesse au sol? Ce 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 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.

Fred Larson
la source
1
La suggestion de déclarer un structsemblable Velocityest une bonne idée. Cependant, une préoccupation est qu'elle pollue l'espace de noms. Je suppose qu'avec C ++ 11, le structpeut avoir un nom de type long et on peut l'utiliser auto result = calculateResultingVelocity(...).
Hugues
5
+1. Une fonction doit renvoyer une "chose", pas un "tuple de choses" ordonné en quelque sorte.
DevSolar
1
Je préfère les structures aux std :: pairs / std :: tuples pour les raisons décrites dans cette réponse. Mais je n'aime pas non plus l'espace de noms "pollution". La solution idéale pour moi serait de retourner une structure anonyme comme 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 comme struct my_func_result_t...
anton_rh
2
@anton_rh: C ++ 14 permet de renvoyer des types locaux avec auto, auto result = my_func();est donc facilement accessible.
ildjarn
4
Il y a une quinzaine d'années, lorsque nous avons découvert le boost, nous utilisions beaucoup le tuple car il est assez pratique. Au fil du temps, nous avons rencontré l'inconvénient de la lisibilité, en particulier pour les tuples ayant le même type (par exemple, tuple <double, double>; lequel est lequel). Ces derniers temps, nous avons plutôt l'habitude d'introduire une petite structure POD où au moins le nom de la variable membre indique quelque chose de sensé.
gast128
24
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 de struct, mais déjà définie pour vous, et prête à s'adapter à deux types de données.

James Curran
la source
3
Cela fonctionnera pour mon exemple simple. En général, cependant, il peut y avoir plus de deux valeurs renvoyées.
Fred Larson
5
Aussi pas auto-documenté. Vous souvenez-vous quel registre x86 est le reste pour DIV?
Mark
1
@Mark - Je conviens que les solutions positionnelles peuvent être moins maintenables. Vous pouvez rencontrer le problème "permute and baffle".
Fred Larson
16

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, je choisirais une structure ou une instance de classe.
  • S'ils ne sont pas vraiment liés et ne peuvent pas être regroupés dans une classe / structure, vous devriez peut-être refactoriser votre méthode en deux.
  • En fonction de la taille en mémoire des valeurs que vous renvoyez, vous souhaiterez peut-être renvoyer un pointeur vers une instance ou une structure de classe, ou utiliser des paramètres de référence.
Stewart Johnson
la source
1
J'aime votre réponse et votre dernière puce me rappelle quelque chose que je viens de lire que le passage par la valeur est devenu beaucoup plus rapide en fonction des circonstances, ce qui rend cela plus compliqué ... cpp-next.com/archive/2009/08/want-speed-pass par valeur
sage
12

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 ..

Bill K
la source
10

Il existe un précédent pour renvoyer des structures dans le standard C (et donc C ++) avec les fonctions div, ldiv(et, en C99 lldiv) 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.

Jonathan Leffler
la source
10

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 .

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 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 manystruct 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'

Johan Lundberg
la source
6

Il existe de nombreuses façons de renvoyer plusieurs paramètres. Je vais être exagéré.

Utilisez des paramètres de référence:

void foo( int& result, int& other_result );

utiliser les paramètres du pointeur:

void foo( int* result, int* other_result );

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:

template<class T>
struct out {
  std::function<void(T)> target;
  out(T* t):target([t](T&& in){ if (t) *t = std::move(in); }) {}
  out(std::optional<T>* t):target([t](T&& in){ if (t) t->emplace(std::move(in)); }) {}
  out(std::aligned_storage_t<sizeof(T), alignof(T)>* t):
    target([t](T&& in){ ::new( (void*)t ) T(std::move(in)); } ) {}
  template<class...Args> // TODO: SFINAE enable_if test
  void emplace(Args&&...args) {
    target( T(std::forward<Args>(args)...) );
  }
  template<class X> // TODO: SFINAE enable_if test
  void operator=(X&&x){ emplace(std::forward<X>(x)); }
  template<class...Args> // TODO: SFINAE enable_if test
  void operator()(Args...&&args){ emplace(std::forward<Args>(args)...); }
};

alors nous pouvons faire:

void foo( out<int> result, out<int> other_result )

et tout va bien. foon'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:

struct foo_r { int result; int other_result; };
foo_r foo();

whick fonctionne bien dans chaque version de C ++, et cela permet également:

auto&&[result, other_result]=foo();

à 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:

std::tuple<int, int> foo();

qui a l'inconvénient que les paramètres ne sont pas nommés. Cela permet au:

auto&&[result, other_result]=foo();

ainsi que. Antérieur à on peut plutôt faire:

int result, other_result;
std::tie(result, other_result) = foo();

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:

void foo( std::function<void(int result, int other_result)> );

et maintenant les appelants font:

foo( [&](int result, int other_result) {
  /* code */
} );

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:

void get_all_values( std::function<void(int)> value )

le valuerappel 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.

void foo( std::function<void(int, std::function<void(int)>)> result );

dont l'utilisation ressemble à:

foo( [&](int result, auto&& other){ other([&](int other){
  /* code */
}) });

qui permettrait des relations multiples entre resultetother .

Encore une fois avec des valeurs uniforn, nous pouvons le faire:

void foo( std::function< void(span<int>) > results )

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.

void foo( std::function< void(span<int>) > results ) {
  int local_buffer[1024];
  std::size_t used = 0;
  auto send_data=[&]{
    if (!used) return;
    results({ local_buffer, used });
    used = 0;
  };
  auto add_datum=[&](int x){
    local_buffer[used] = x;
    ++used;
    if (used == 1024) send_data();
  };
  auto add_data=[&](gsl::span<int const> xs) {
    for (auto x:xs) add_datum(x);
  };
  for (int i = 0; i < 7+(1<<20); ++i) {
    add_datum(i);
  }
  send_data(); // any leftover
}

Maintenant, std::functionc'est un peu lourd pour cela, car nous le ferions dans des environnements sans allocation sans frais généraux. Nous voudrions donc un function_viewqui n'alloue jamais.

Une autre solution est:

std::function<void(std::function<void(int result, int other_result)>)> foo(int input);

où au lieu de prendre le rappel et de l'invoquer, foorenvoie à 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 etcoroutines, vous pouvez créer fooun 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:

template<class...Args>
struct broadcaster;

broadcaster<int, int> foo();

vous permet de créer un fooqui 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.

foo( int_source )( int_dest1, int_dest2 );

alors ce code ne fait rien tant qu'il int_sourcen'a pas d'entiers pour le fournir. Quand c'est le cas, int_dest1et int_dest2commencez à recevoir les résultats.

Yakk - Adam Nevraumont
la source
Cette réponse contient plus d'informations que les autres réponses! en particulier, des informations sur les auto&&[result, other_result]=foo();fonctions renvoyant à la fois des tuples et des structures. Merci!
jjmontes
J'apprécie cette réponse exhaustive, d'autant plus que je suis toujours coincé avec C ++ 11 et que je ne peux donc pas utiliser certaines des solutions les plus modernes que d'autres proposent.
GuyGizmo
5

Utilisez une structure ou une classe pour la valeur de retour. L'utilisation std::pairpeut fonctionner pour l'instant, mais

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

Le 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_resultstructure 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.

Michel
la source
4

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

dmityugov
la source
4

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.

#  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 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.

PRAFUL ANAND
la source
4
Avec C ++ 14, vous pouvez également utiliser le autotype de retour calpour rendre cela encore plus propre. (OMI).
sfjac
3

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.

John Dibling
la source
2

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).

marque
la source
2

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.

Patrick
la source
1
Je pense que c'est un peu déroutant. Quelqu'un qui lit le code qui l'appelle voit "diviser (a, b, c);". Il n'y a aucune indication que c est un intervalle jusqu'à ce qu'ils recherchent la signature. Mais c'est une peur générale des paramètres de référence non const, plutôt que particulière à cette question.
Steve Jessop
2

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.

class div{
   public:
      int remainder;

      int quotient(int dividend, int divisor){
         remainder = ...;
         return ...;
      }
};
Roland
la source
1
Je pense qu'il y a des cas où cela est inefficace. Par exemple, vous avez une seule boucle for qui génère plusieurs valeurs de retour. Si vous divisez ces valeurs en fonctions distinctes, vous devrez parcourir la boucle une fois pour chaque valeur.
jiggunjer
1
@jiggunjer Vous pouvez exécuter la boucle une fois et stocker les plusieurs valeurs de retour dans des membres de données de classe distincts. Cela souligne la flexibilité du concept de POO.
Roland
2

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:

int divide(int a,int b,int quo,int &rem)
Anchit Rana
la source
Ne l'ai-je pas mentionné dans la question elle-même? Voir également mes objections dans ma réponse .
Fred Larson
1

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:

include "boost/tuple/tuple.hpp"

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

{
  return make_tuple(dividend / divisor,dividend % divisor )
}
AndyUK
la source
1

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…)

Rohit Hajare
la source
1

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:

static struct SomeReturnType {int a,b,c; string str;} SomeFunction()
{
  return {1,2,3,string("hello world")}; // make sure you return values in the right order!
}

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.

 SomeReturnType st = SomeFunction();
 cout << "a "   << st.a << endl;
 cout << "b "   << st.b << endl;
 cout << "c "   << st.c << endl;
 cout << "str " << st.str << endl;

Ce n'est certainement pas la plus jolie façon de le faire, mais cela fonctionnera.

Carsten
la source
-2

Voici un exemple complet de ce type de solution de problème

#include <bits/stdc++.h>
using namespace std;
pair<int,int> solve(int brr[],int n)
{
    sort(brr,brr+n);

    return {brr[0],brr[n-1]};
}

int main()
{
    int n;
    cin >> n;
    int arr[n];
    for(int i=0; i<n; i++)
    {
        cin >> arr[i];
    }

    pair<int,int> o=solve(arr,n);
    cout << o.first << " " << o.second << endl;

    return 0;
}
Shaonsani
la source