C++ -- return x,y; 这是什么意思?

48

我已经学习了几年的C和C++编程,现在正在参加大学课程学习,我们的教材提供了一个函数示例:

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

我从未见过这样的语法。事实上,我从未见过除了参数列表之外使用,运算符。如果y总是被返回,那么这有什么意义呢?是否存在需要创建这样的返回语句的情况?

(另外,我也标记了C,因为它适用于两种语言,尽管我的书特别讲解的是C++)


14
这本书是哪本?我无法想象任何合理的理由来做这件事。 - Stephen
7
"D. Malik,《C++程序设计:包括数据结构》第四版。Course Technology Incorporated,2007年,ISBN 1-4188-3640-0" 我不建议任何人阅读此书,因为作者似乎对C++并没有很牢固的掌握,提到了一些像“变量声明总是放在函数顶部,无论如何”的东西。 - Earlz
124
扔掉这本书。 - Daniel Daranas
3
@Daniel,我真的很想帮忙,但是这是为了获得我的大学学分所必需的。 - Earlz
5
@Frank V说:“即使这样做也没有意义 - 这些变量即将超出作用域。”@Earlz问:“这是否仅仅是为了演示这不会返回两个值呢?给出一个不应该做的例子?” - Cascabel
显示剩余14条评论
18个回答

53
根据 C FAQ,在表达式:

e1 , e2

中,逗号操作符的含义可以精确地表述为:“评估子表达式e1,然后评估e2;表达式的值是e2的值。” 因此,e1 最好涉及赋值、自增++、自减--、函数调用或其他某种副作用,否则它将计算一个会被丢弃的值。

所以我同意你的看法,除了证明这是有效的语法之外没有其他意义。

如果你想在 C 或 C++ 中返回两个值,你可以创建一个包含 x 和 y 成员的结构体,然后返回该结构体:

struct point {int x; int y;};

接下来,您可以定义一个类型和帮助函数,以便在struct中轻松返回两个值:

typedef struct point Point;

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

然后修改您的原始代码以使用帮助函数:

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

最后,你可以尝试一下:

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

这个示例可以在C和C++中编译通过。尽管如Mark所建议的,在C++中你可以为point结构定义一个构造函数,这可以提供一种更优雅的解决方案。


顺便说一下,在支持直接返回多个值的语言(如Python)中,这种能力非常棒:

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

>>> foo()
(0, 20)

1
你的 point 结构体需要一个接受两个 int 参数的构造函数。 - Mark Ransom
@Mark - struct 只是一个例子 - 话说,你能详细说明一下吗?严格来说,在 C 中没有构造函数。不过,无论如何,这段代码在 C 和 C++ 中都可以编译通过,并且 xy 成员可以直接被代码访问。 - Justin Ethier
在C语言中,你是正确的,没有构造函数可用。在C++中,构造函数允许你执行return point(x,y);。抱歉我说话有点神秘。 - Mark Ransom
1
我想指出你在没有意义的情况下表达自己的观点这一点让我感到很有趣。 - Jason Rice

23

在参数列表中的逗号只是用来分隔参数,不同于逗号操作符。像你例子中的逗号操作符会评估 x 和 y 两个值,然后丢弃 x。

在这种情况下,我猜想这是某人试图返回两个值时犯的错误,并且不知道该怎么做。


20

逗号运算符主要用于像这样的for语句中:

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

第一个逗号不是逗号运算符,它是声明语法的一部分。第二个逗号运算符。


18
这毫无意义。为什么这个答案会被点赞,并且为什么被选为正确答案!它完全与问题无关。 - vivekian2
在这种情况下,必须使用逗号来增加 ij。还有其他一些情况,如果您想强调两个语句非常紧密地联系在一起,不应拆分,那么可能会使用逗号而不是分号。例如:std::memcpy(data, moredata, moresize), datasize+=size;。我喜欢将这些语句放在一起,以避免有人意外移动或删除第二部分。 - André Caron
1
@Joel:如果你回答的问题被点赞或被采纳,你可以获得徽章。 - Sebastian Mach
2
我认为问题的真正含义是“为什么要有逗号运算符?”而不是“为什么在返回语句中使用逗号运算符?”显然,楼主同意这是他真正想问的问题。 - Joel

9

这并没有真正回答原始问题,但可能对一些人很有趣。如果你想在C++中同时返回两个值,你需要像这样编写代码(并且需要一个c++0x编译器)。

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

您可以像这样访问它 -
tuple<int, int> data = foo();
int a = get<0>(data);
int b = get<1>(data);

1
个人而言,我更喜欢这种写法:int a = 0, b = 0; tie(a,b) = foo(); - Edward Strange
我不知道tie,我得试试看。 - jcoder
1
在C++03编译器中,只需返回一个pair。当然,这并不能直接推广到超过两个值的情况。 - David Thornley

8
 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


4
那不是C++的风格。我的意思是,它可以编译,但那太难以阅读了。呃。 - clahey
1
@clahey:那么你会讨厌这个:http://www.boost.org/doc/libs/1_42_0/libs/assign/doc/index.html - kennytm
6
我认为增强功能确实存在疑问,但你所提出的更糟糕的问题在于它。例如:"return (Point)1, 2, 3;" 返回(1,3)。"return (Point)1" 返回(1,0)。这两种情况都不像是直观的行为。此外,你的记号并没有节省任何东西。如果你只是声明一个接受两个整数的适当构造函数,你可以写成 "return Point(x, y);"。这样做并没有节省一分打字,而且更易读。增强功能至少可以大大简化打字。 - clahey
2
@clahey:请注意,我并不是在提倡这种风格(看到“:p”了吗?),我只是想表明逗号运算符可能被滥用了(我无法放置多行注释)。 - kennytm
那真是太精妙的“邪恶”了。是否有类似 IOCCC 这样的 C++ 竞赛? - John Bode

5

就像这里的每个人都认为它是毫无意义的,我也不反对,只看这个例子,我猜测情况并不会好到哪里去:

作者在函数内得到了一个编译器警告,提示变量x没有被使用,而这是消除警告的简单方法。


我刚用g++尝试了一下,如果你使用-Wall选项会有一个警告。我不确定是要赞扬作者在将代码放入书中之前编译它,还是谴责作者想出这个解决方案。虽然,如果作者真的利用这个机会谈论编译器警告等问题,那可能会很有趣。 - clahey
我收回之前的话。我刚刚按照给出的代码尝试了一下,但仍然会出现警告。也许他使用的是不同的编译器。 - clahey
他还有一本关于Java的书(这里就有一个红旗),所以可能使用MVC++。 - Earlz
看起来VS6.0可能是最有可能的选择,如果移除"x,"并且将警告级别设置为4级(最高级别),它会给出一个警告C4189:本地变量已初始化但未被引用。 - Joel Rondeau
如果这是作者的意图,那么这是一个糟糕的想法...编写看起来返回两个值的代码,而实际上只返回一个值,这样做会带来更多的伤害而不是好处。 - Jeremy Friesner

4
这是逗号运算符(,)。
同时计算x和y的表达式。整个表达式的结果是y,即后一个值。
很难说为什么在这里使用它。我猜是为了演示目的。显然,函数可以重构为:
int foo()
{
  return 20;
}

3
这里给出的答案是......似乎没有任何意义。 - Cascabel
@Jefromi:我评论时那里还没有。 - Michael Myers

4

这种语法可以用来保存if语句的额外作用域括号。例如,通常你会写以下代码:

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

这可以被以下内容替代:
if (someThing == true)
    return a = 1, b = 2, true;

我认为这种编码风格的使用更多是出于想要炫耀而不是写出整洁代码的动机。


嘿,那是一个有趣的用例。 - Earlz
我认为这种动机通常不是来自于想要炫耀,而是希望通过节省读者数小时的垂直滚动工作来提高代码的可读性。 - Kaiserludi

3

这段代码看起来很糟糕。虽然它在C/C++中可能是有效的语法,但我想不出你为什么要这样做。

如果你想返回x和y,更好的方法是在C++中定义一个"Point"类或结构体,其中包含x和y属性,然后返回该类或结构体。另一种选择是通过引用传递x和y,然后在方法中适当地设置值。

如果该方法只返回y,则只需使用"return y;"。如果在返回语句之前需要"计算" x,应该在单独的一行上完成。


2

这个return语句没有任何意义。

如果x被声明为volatile,它会强制访问(因为至少在C++中,对volatile变量的引用被认为是外部可观察行为),但它并没有被声明为volatile

如果不是x,而是一些具有副作用的计算,它将执行该计算,然后返回y。然而,非volatilex没有副作用。实现不需要执行任何没有副作用或外部可观察行为的代码。逗号运算符执行逗号左侧的任何内容,忽略结果,并执行和保留右侧的值(除了在这种情况下可以忽略操作符左侧)。

因此,return x, y;语句与return y;完全相同。如果x不是完全没有意义的东西,那么以x; return y;的方式编写会更好,这是完全相同的。这样做不会让人感到混乱。


即使将 x 替换为具有副作用的某个表达式,也没有必要像那样将其塞入返回语句中。只需将其作为单独的语句:expr_with_side_effects; return y; - Keith Thompson
通常:是的,会读起来更清晰。但是对于 constexpr 函数(C++14 之前)呢? - DaveFar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接