逗号运算符的正确用法是什么

What is the proper use of the comma operator?

本文关键字:用法 是什么 运算符      更新时间:2023-10-16

我看到了这段代码:

if (cond) {
    perror("an error occurred"), exit(1);
}

你为什么要这样做?为什么不只是:

if (cond) {
    perror("an error occurred");
    exit(1);
}

在您的示例中,它根本没有理由。它有时在写成

if(cond)
  perror("an error occured"), exit(1) ;

--那么你就不需要大括号了。但这是对灾难的邀请。

逗号运算符是将两个或多个表达式放在引用只允许一个表达式的位置。在您的情况下,无需使用它;在其他情况下,例如在 while 循环中,它可能很有用:

while (a = b, c < d)
  ...

其中,while 循环的实际"计算"仅由最后一个表达式控制。

号运算符的合法情况很少见,但它们确实存在。一个例子是,当您希望在条件评估中发生某些事情时。例如:

std::wstring example;
auto it = example.begin();
while (it = std::find(it, example.end(), L''), it != example.end())
{
    // Do something to each backslash in `example`
}

它也可以在只能放置一个表达式但希望发生两件事的地方使用。例如,以下循环在 for 循环的第三个组件中递增 x 并递减 y:

int x = 0;
int y = some_number;
for(; x < y; ++x, --y)
{
    // Do something which uses a converging x and y
}

不要去寻找它的用途,但如果它是合适的,不要害怕使用它,如果你看到别人使用它,不要被扔到循环中。如果你有两件事没有理由不是单独的语句,请使它们成为单独的语句,而不是使用逗号运算符。

逗号运算符的主要用途是混淆;它允许做两个读者只期望的东西。 最常见的之一用途 - 为条件添加副作用,属于此范围类别。 有少数情况可能被认为是有效的,然而:

用于在K&R中呈现它的那个:递增2for循环中的变量。 在现代代码中,这可能发生在函数类似于std::transformstd::copy,其中输出迭代器与输入迭代器对称递增。 (更常见的是当然,这些函数将包含一个while循环,其中循环末尾单独语句中的增量。 在这样的在这种情况下,使用逗号而不是两个语句是没有意义的。

想到的另一种情况是输入参数的数据验证在初始值设定项列表中:

MyClass::MyClass( T const& param )
    : member( (validate( param ), param) )
{
}

(这假设validate( param )将引发异常,如果有些不对劲。 这种用途并不是特别有吸引力,尤其是因为它需要额外的括号,但没有太多选择。

最后,我有时会看到约定:

ScopedLock( myMutex ), protectedFunction();

,这避免了为ScopedLock发明名称。 要告诉事实是,我不喜欢它,但我见过它被使用,以及替代方案添加额外的大括号以确保立即ScopedLock被破坏也不是很漂亮。

通过举一些例子可以更好地理解这一点:

第一:考虑一个表达式:

   x = ++j;

但就目前而言,如果我们需要分配一个临时调试值,那么我们可以编写。

   x = DEBUG_VALUE, ++j; 

第二:
逗号,运算符经常用于for()循环中,例如:

for(i = 0, j = 10; i < N; j--, i++) 
 //      ^                   ^     here we can't use ;  

第三:
再举一个例子(实际上人们可能会发现这样做很有趣(:

if (x = 16 / 4), if remainder is zero then print  x = x - 1;  
if (x = 16 / 5), if remainder is zero then print  x = x + 1;

它也可以在一个步骤中完成;

  if(x = n / d, n % d) // == x = n / d; if(n % d)
    printf("Remainder not zero, x + 1 = %d", (x + 1));
  else
    printf("Remainder is zero,  x - 1 = %d", (x - 1));

PS:知道有时使用,运算符是灾难性的,这可能也很有趣。例如,在问题 Strtok 用法中,代码不起作用,错误地,OP 忘记写函数的名称,而不是写tokens = strtok(NULL, ",'");,他写了tokens = (NULL, ",'");,他没有收到编译错误 - 但这是一个有效的表达式,tokens = ",'";在他的程序中导致了无限循环。

逗号运算符允许在预期的地方对表达式进行分组。

例如,在某些情况下它可能很有用:

// In a loop
while ( a--, a < d ) ...

但是在您的情况下,没有理由使用它。这会令人困惑...就是这样。。。

在您的情况下,这只是为了避免大括号:

if(cond)
    perror("an error occurred"), exit(1);
// =>
if (cond)
{
    perror("an error occurred");
    exit(1);
}

指向逗号运算符文档的链接。

运算符 (( 的实际用途似乎很少。

Bjarne Stroustrup,《C++的设计与演变》

逗号的大部分常用法都可以在维基百科文章Comma_operator#Uses中找到。

我在使用 boost::assign 时发现了一个有趣的用法,它明智地重载了运算符,使其表现为逗号分隔的值列表,可以推送到矢量对象的末尾

#include <boost/assign/std/vector.hpp> // for 'operator+=()'
using namespace std;
using namespace boost::assign; // bring 'operator+=()' into scope
{
    vector<int> values;  
    values += 1,2,3,4,5,6,7,8,9; // insert values at the end of the container
}

不幸的是,一旦编译器开始支持统一初始化,上述在原型设计中流行的用法现在看起来就会过时。

所以这让我们回到

运算符 (( 的实际用途似乎很少。

Bjarne Stroustrup,《C++的设计与演变》

在您的情况下,逗号运算符是无用的,因为它本可用于避免大括号,但事实并非如此,因为作者已经放置了它们。因此,它是无用的,可能会令人困惑

如果要在

条件为真时执行两个或多个指令,则对行程运算符可能很有用。 但请记住,由于逗号运算符从左到右的计算规则(我的意思是在括号内(,返回值将是最右的表达式

例如:

a<b?(x=5,b=6,d=i):exit(1);

boost::assign会重载逗号运算符以实现这种语法:

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;