C++ — return x,y; What is the point?

岁酱吖の 提交于 2019-11-27 21:46:53
Joel

The comma operator is primarily used in for statements like so:

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

The first comma is not a comma operator, it's part of the declaration syntax. The second is a comma operator.

According to the C FAQ:

Precisely stated, the meaning of the comma operator in the general expression

e1 , e2

is "evaluate the subexpression e1, then evaluate e2; the value of the expression is the value of e2." Therefore, e1 had better involve an assignment or an increment ++ or decrement -- or function call or some other kind of side effect, because otherwise it would calculate a value which would be discarded.

So I agree with you, there is no point other than to illustrate that this is valid syntax, if that.

If you wanted to return both values in C or C++ you could create a struct containing x and y members, and return the struct instead:

struct point {int x; int y;};

You can then define a type and helper function to allow you to easily return both values within the struct:

typedef struct point Point;

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

And then change your original code to use the helper function:

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

And finally, you can try it out:

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

This example compiles in both C and C++. Although, as Mark suggests below, in C++ you can define a constructor for the point structure which affords a more elegant solution.


On a side note, the ability to return multiple values directly is wonderful in languages such as Python that support it:

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

>>> foo()
(0, 20)

The comma in parameter lists is just there to separate the parameters, and is not the same as the comma operator. The comma operator, as in your example, evaluates both x and y, and then throws away x.

In this case, I would guess that it is a mistake by someone who tries to return two values, and didn't know how to do it.

This doesn't really answer the original question at all but might be of interest to some people, but if you wanted to it to return both in C++ you'd need to write it like this (and would need a c++0x compiler)

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

The access it like this -

tuple<int, int> data = foo();
int a = get<0>(data);
int b = get<1>(data);
 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

Much like everyone commenting here thinks it is pointless and I don't disagree, just looking at the example, I'm going to make a guess that's not much better:

The writer was getting a compiler warning about x not being used within the function, and this was an easy way to get the warning to go away.

This is the comma operator (,).

Both expressions x and y are evaluated. The result of the overall expression is y, i.e., the latter value.

It's hard to say why it is used here. I guess, for demonstration purposes. Clearly the function could be refactored to:

int foo()
{
  return 20;
}

This syntax can be used to save additional scope brackets of an if- statement. E.g. normally you would write the following:

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

This can be replaced by the following:

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

I think the usage of this coding style is rather motivated by the urge for posing than for writing clean code.

That looks like a terrible example of code. It might be valid syntax in C/C++, but I can't think of a reason why you'd ever want to do that.

If you want to return both x and y, a better way to do it in C++ would be to define a "Point" class or struct with x and y properties, and return that. Another option would be to pass in x and y by reference, then set the values appropriately in the method.

If the method is going to just return y, I would just "return y;". If x needs to be "evaluated" before the return statement, it should be done on a separate line.

There is no point in that return statement.

If x were declared volatile, it would force an access (since at least in C++ references to volatile variables are considered to be externally observable behavior), but it isn't.

If, instead of x, there was some sort of calculation with side effects, it would do that calculation and then return y. However, a non-volatile x has no side effects. The implementation is not required to execute any code that has no side effects or externally observable behavior. The comma operator executes whatever is on the left side of the comma, disregards the result, and executes and keeps the value of the right side (except that it's free to ignore the left side of the operator in this case).

Therefore, the return x, y; statement is the exact same thing as return y;. If x wasn't just a completely meaningless thing to execute, it would be stylistically better to write it as x; return y;, which is the precise same thing. It wouldn't be nearly as confusing that way.

On the one hand, it could be an honest mistake on the part of the writer.

On the other hand, the writer might be explaining syntactically correct correct code, versus compiler warnings.

Either way, the only way to return multiple results would be to define a class and use its instance, or perhaps an array or collection.

This is the comma operator. Such syntax can be used to disable warning from compiler about unused variable x.

Outside of for loops the other major user of this comman operator (as apposed to the function call version) is in macros that return a value after doing some stuff. These are other ways to do this now, but I think that the comman operator used to be the cleanest way.

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

Please note that this macro is a bad example of macros in general because it repeats x and probably for other reasons. Use of the comma operator in this fashion should be rare. The example from your book was probably an attempt to make a code exampe fit within the number of lines available for that example.

Is there a case where a return statement would need to be created like this?

IMO, I would never use multiple returns in a function like the book example. It violates structured design. Nevertheless, there are many programmers that do! Debugging someone else's code I have assigned a value to a global variable in each return statement so I could figure out which return executed.

I have seen this syntax used in C to do housekeeping when returning midway in an operation. Definitely not maintainable code:

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;

}

The book is trying to eliminate potential confusion of people who learned other languages before C++. In many languages, you can return multiple values using similar syntax. In C++, it will compile without warning (unless you specify -Wall or -Wunused-value), but it won't work the way you might expect if you were accustomed to those other languages. It will just return the last value.

However, it seems the author caused more confusion than he prevented, since there's no readable situation to use such syntax in a return statement in C++ except by accidentally using it like another language. He's warning about usage that wouldn't occur to most people to try. If you did, though, it would be super confusing to debug, since the multiple assignment statement int x, y = foo() also compiles just fine.

Bottom line: always use -Wall and fix what it warns you about. C++ syntax allows you to write many things that don't make sense.

When used with the return keyword, the comma operator returns the last value, which is initially confusing but can make things more concise.

For example, the following program will exit with a status code of 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;
}

When compiling and executing with the following, you will see the output below.

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

If y is always returned though, then what is the point?

The point is the side effect of x (ie of the left hand side of the comma operator). See e.g. Justin Ethier answer for details.

Is there a case where a return statement would need to be created like this?

An example is for a constexpr function in C++11 until C++14: such a function may not contain arbitrary statements, but exactly one return statement.

Here is a code sample from Patrice Roys “The Exception Situation" at CppCon 2016:

constexpr int integral_div(int num, int denom) {
    return assert(denom != 0), num / denom;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!