web-dev-qa-db-fra.com

C++ - retourne x, y; Dans quel but?

Je programme en C et C++ depuis quelques années et maintenant, je suis en train de suivre un cours de niveau universitaire et notre livre avait une fonction comme celle-ci, par exemple:

int foo(){
  int x=0;
  int y=20;
  return x,y; //y is always returned
}

Je n'ai jamais vu une telle syntaxe. En fait, je n'ai jamais vu l'opérateur , utilisé en dehors des listes de paramètres. Si y est toujours renvoyé, quel est le but? Y a-t-il un cas où une déclaration de retour devrait être créée comme ceci? 

(De plus, j'ai aussi marqué C parce que cela s'applique aux deux, bien que mon livre soit spécifiquement C++)

46
Earlz

L'opérateur virgule est principalement utilisé dans les instructions for comme ceci:

for( int i=0, j=10; i<10; i++, j++ )
{
    a[i] = b[j];
}

La première virgule n'est pas un opérateur de virgule, elle fait partie de la syntaxe de déclaration. Le second est un opérateur de virgule.

20
Joel

Selon le C FAQ :

Précisément précisé, la signification de l'opérateur virgule dans l'expression générale

e1, e2

est "évalue la sous-expression e1, puis évalue e2; la valeur de l'expression est la valeur de e2". Par conséquent, e1 devrait impliquer une affectation ou un incrément ++ ou décrément - ou un appel de fonction ou un autre type d’effet secondaire, car sinon il calculerait une valeur qui serait ignorée. 

Donc je suis d'accord avec toi il n'y a aucun intérêt autre que pour illustrer que c'est une syntaxe valide, si cela. 

Si vous voulez renvoyer les deux valeurs en C ou C++, vous pouvez créer un struct contenant les membres x et y et renvoyer la structure à la place:

struct point {int x; int y;};

Vous pouvez ensuite définir un type et une fonction d'assistance pour vous permettre de renvoyer facilement les deux valeurs dans la variable struct:

typedef struct point Point;

Point point(int xx, int yy){
  Point p;
  p.x = xx;
  p.y = yy;
  return p;
}

Et puis modifiez votre code d'origine pour utiliser la fonction d'assistance:

Point foo(){
  int x=0;
  int y=20;
  return point(x,y); // x and y are both returned
}

Et enfin, vous pouvez l'essayer:

Point p = foo();
printf("%d, %d\n", p.x, p.y);

Cet exemple est compilé à la fois en C et en C++. Bien que, comme Mark le suggère ci-dessous, en C++, vous pouvez définir un constructeur pour la structure point qui offre une solution plus élégante.


Par ailleurs, la possibilité de renvoyer directement plusieurs valeurs est formidable dans des langages tels que Python qui le prennent en charge:

def foo():
  x = 0
  y = 20
  return x,y # Returns a Tuple containing both x and y

>>> foo()
(0, 20)
51
Justin Ethier

La virgule dans les listes de paramètres sert uniquement à séparer les paramètres et n'est pas identique à l'opérateur de virgule. L'opérateur virgule, comme dans votre exemple, évalue x et y, puis jette x.

Dans ce cas, je suppose que c'est une erreur de la part de quelqu'un qui essaie de renvoyer deux valeurs et qui ne sait pas comment le faire.

23

Cela ne répond pas du tout à la question initiale, mais pourrait intéresser certaines personnes, mais si vous voulez le renvoyer en C++, vous devez l'écrire comme ceci (et vous aurez besoin d'un compilateur c ++ 0x )

Tuple<int, int> foo()
{
    int x = 0;
    int y = 20;
    return make_Tuple(x, y);
}

L'accès comme ça -

Tuple<int, int> data = foo();
int a = get<0>(data);
int b = get<1>(data);
9
jcoder
 struct Point {
   int x, y;
   Point(int x_) : x(x_), y(0) {}
   Point(const Point& p) : x(p.x), y(p.y) {}
   Point operator, (int y_) const { Point p=*this; p.y = y_; return p; }
 };

 Point get_the_point () {
    int x = 0;
    int y = 20;
    return (Point)x, y;
 }

: p

8
kennytm

Comme tout le monde le commente ici, cela ne sert à rien et je ne suis pas en désaccord. En regardant l'exemple, je vais faire une supposition qui ne vaut guère mieux:

L’écrivain recevait un avertissement du compilateur indiquant que x n’était pas utilisé dans la fonction et que c’était un moyen facile d’obtenir l’avertissement.

5
Joel Rondeau

Cette syntaxe peut être utilisée pour enregistrer des crochets de portée supplémentaires d'une instruction if-. Par exemple. normalement vous écririez ce qui suit:

if (someThing == true)
{
    a = 1;
    b = 2;
    return true;
}

Ceci peut être remplacé par ce qui suit:

if (someThing == true)
    return a = 1, b = 2, true;

Je pense que l'utilisation de ce style de codage est plutôt motivée par l'envie de poser que par l'écriture de code propre.

4
florg

C'est l'opérateur virgule (,) .

Les deux expressions x et y sont évaluées. Le résultat de l'expression globale est y, c'est-à-dire la dernière valeur.

Il est difficile de dire pourquoi il est utilisé ici. Je suppose, à des fins de démonstration. Clairement, la fonction pourrait être remaniée pour:

int foo()
{
  return 20;
}
4
Daniel Daranas

Cela ressemble à un terrible exemple de code. C'est peut-être une syntaxe valide en C/C++, mais je ne vois pas pourquoi vous voudriez le faire.

Si vous voulez retourner x et y, une meilleure façon de le faire en C++ serait de définir une classe ou une structure "Point" avec des propriétés x et y, et de le retourner. Une autre option serait de passer x et y par référence, puis de définir les valeurs de manière appropriée dans la méthode.

Si la méthode retourne simplement y, je voudrais simplement "retourner y;". Si x doit être "évalué" avant l'instruction de retour, vous devez le faire sur une ligne distincte.

3
Andy White

Il n'y a aucun point dans cette déclaration de retour.

Si x était déclaré volatile, cela forcerait l'accès (au moins en C++, les références aux variables volatile sont considérées comme un comportement observable de l'extérieur), mais ce n'est pas le cas.

Si, au lieu de x, il existait une sorte de calcul avec des effets secondaires, il le ferait puis renverrait y. Cependant, une non-volatilex n'a aucun effet secondaire. L'implémentation n'est pas obligée d'exécuter un code qui n'a pas d'effets secondaires ou de comportement observable de manière externe. L'opérateur virgule exécute tout ce qui se trouve à gauche de la virgule, ignore le résultat, exécute et conserve la valeur du côté droit (sauf qu'il est libre d'ignorer le côté gauche de l'opérateur dans ce cas).

Par conséquent, l'instruction return x, y; est exactement la même chose que return y;. Si x n'était pas une chose totalement dépourvue de sens à exécuter, il serait préférable stylistiquement de l'écrire sous la forme x; return y;, ce qui est exactement la même chose. Ce ne serait pas aussi déroutant de cette façon.

2
David Thornley

En dehors des boucles for, l'autre utilisateur principal de cet opérateur commun (comme indiqué dans la version de l'appel de fonction) se trouve dans des macros qui renvoient une valeur après avoir effectué certaines opérations. Ce sont d’autres manières de le faire maintenant, mais je pense que l’opérateur commun était jadis le moyen le plus propre.

#define next(A, x, y, M) ((x) = (++(y))%(M) , A[(x)])

Veuillez noter que cette macro est un mauvais exemple de macros en général car elle répète x et probablement pour d'autres raisons . L'utilisation de l'opérateur de virgule de cette manière devrait être rare. L'exemple de votre livre était probablement une tentative visant à adapter un code exampe au nombre de lignes disponibles pour cet exemple.

1
nategoose

C'est l'opérateur de virgule. Cette syntaxe peut être utilisée pour désactiver l'avertissement du compilateur à propos de la variable inutilisée x.

1
osgx

D'une part, cela pourrait être une erreur honnête de la part de l'écrivain.

D'autre part, le rédacteur pourrait expliquer le code correct syntaxiquement correct, par opposition aux avertissements du compilateur.

Dans les deux cas, le seul moyen de renvoyer plusieurs résultats consiste à définir une classe et à utiliser son instance, voire un tableau ou une collection.

1
Olaseni

Y a-t-il un cas où une déclaration de retour devrait être créée comme ceci? 

Messieurs, je n’utiliserais jamais plusieurs déclarations dans une fonction telle que l’exemple de livre. Cela viole la conception structurée. Néanmoins, beaucoup de programmeurs le font! Débogage du code de quelqu'un d'autre J'ai affecté une valeur à une variable globale dans chaque instruction return afin de déterminer le retour exécuté.

0
d hughes

J'ai vu cette syntaxe utilisée en C pour faire le ménage lors d'un retour à mi-parcours d'une opération. Code définitivement non maintenable:

int foo(int y){
  char *x;
  x = (char*)malloc(y+1);
  /** operations */
  if (y>100) return free(x),y;
  /** operations */
  if (y>1000) return free(x),y;

}
0
SRC

Si y est toujours renvoyé, quel est le but? 

Le point est l'effet secondaire de x (c.-à-d. Du côté gauche de l'opérateur de virgule). Voir par exemple Justin Ethier répond pour plus de détails.

Y a-t-il un cas où une déclaration de retour devrait être créée comme ceci? 

Un exemple concerne une fonction constexpr dans C++ 11 jusqu'à C++ 14: une telle fonction peut ne pas contenir d'instructions arbitraires, mais une seule instruction de retour.

Voici un exemple de code extrait de Patrice Roys «The Exception Situation» à la CppCon 2016 :

constexpr int integral_div(int num, int denom) {
    return assert(denom != 0), num / denom;
}
0
DaveFar

Le livre tente d'éliminer la confusion potentielle des personnes ayant appris d'autres langages avant le C++. Dans de nombreuses langues, vous pouvez renvoyer plusieurs valeurs en utilisant une syntaxe similaire. En C++, il sera compilé sans avertissement (à moins que vous n'ayez spécifié -Wall ou -Wunused-value), mais cela ne fonctionnera pas comme vous le souhaiteriez si vous étiez habitué à ces autres langues. Il ne fera que renvoyer la dernière valeur.

Cependant, il semble que l'auteur ait créé plus de confusion qu'il n'en avait empêché, car il n'y a pas de situation lisible pour utiliser une telle syntaxe dans une instruction de retour en C++ sauf en l'utilisant accidentellement comme un autre langage. Il met en garde contre une utilisation que la plupart des gens ne penseraient pas à essayer. Si vous le faisiez cependant, le débogage serait très déroutant, car la déclaration d'affectation multiple int x, y = foo() compile également très bien.

En bout de ligne: utilisez toujours -Wall et corrigez ce qui vous en avertit. La syntaxe C++ vous permet d'écrire beaucoup de choses qui n'ont pas de sens.

0
Karl Bielefeldt

Lorsqu'il est utilisé avec le mot clé return, l'opérateur de virgule renvoie la valeur last qui est initialement déroutant mais peut rendre les choses plus concises.

Par exemple, le programme suivant se termine avec un code d’état de 2.

#include <iostream>
using namespace std;

void a() {
    cout << "a" << endl;
}


int one() {
    cout << "one" << endl;
    return 1;
}

int zero() {
    cout << "zero" << endl;
    return 0;
}

int main() {
    return one(), a(), zero(), 2;
}

Lors de la compilation et de l'exécution avec ce qui suit, vous verrez le résultat ci-dessous. 

michael$ g++ main.cpp -o main.out && ./main.out ; echo $?
one
a
zero
2
0
magnus