【问题标题】:Is it good practice to use the comma operator?使用逗号运算符是一种好习惯吗?
【发布时间】:2012-01-03 10:12:51
【问题描述】:

我最近(实际上仅在 SO 上)遇到了 C/C++ 逗号运算符的使用。据我所知,它在左侧和右侧运算符之间的线上创建了一个序列点,以便您拥有可预测(已定义)的评估顺序。

我有点困惑为什么会在语言中提供它,因为它看起来像是一个补丁,可以应用于最初不应该工作的代码。我发现很难想象它可以在不太复杂(并且需要重构)的地方使用。

有人能解释一下这个语言特性的用途吗?如果有的话,它可以在实际代码中的什么地方使用(在合理的范围内)?

【问题讨论】:

  • 这里有个巧妙的用法:stackoverflow.com/questions/7905905/…
  • @R.MartinhoFernandes:聪明吗?我认为你错过了大局。 “高级”C++ 元编程没有什么聪明之处,只是施虐受虐。

标签: c++ c operators


【解决方案1】:

while()循环的情况下很有用:

while (update_thing(&foo), foo != 0) {
    /* ... */
}

这避免了必须复制update_thing() 行,同时仍保持while() 控制表达式中的退出条件,您希望在其中找到它。它还可以很好地与continue; 搭配使用。

它在编写求值的复杂宏时也很有用。

【讨论】:

  • 谢谢大家 :) 有趣的是,我之前实际上已经在循环中使用过它,但在我问这个问题之前我从来没有想过它是一个运算符。 (我知道,这很明显,所以我觉得有点傻!:p)
  • 同样,我见过while (update_thing(y), test_thing(y))。它允许您将更新和测试分成两个单独的函数以提高可读性,并且在循环中仍然可以理解--while(哦,等一下,让我们更新这个东西,现在好--)这个东西测试为真,做这个东西。
【解决方案2】:

逗号运算符只是分隔表达式,因此您可以做多项事情,而不是只需要一个表达式的事情。它可以让你做类似的事情

             (x)              (y)
for (int i = 0, j = 0; ...; ++i, ++j)

请注意,x 不是逗号运算符,但 y 是。

您真的不必考虑它。它还有一些更神秘的用途,但我不认为它们是绝对必要的,所以它们只是好奇。

【讨论】:

    【解决方案3】:

    for 循环构造中它是有意义的。虽然在这种情况下我通常发现它们更难阅读。

    这对于激怒您的同事和 SO 上的人也非常方便。

    bool guess() {
      return true, false;
    }
    

    【讨论】:

    • 变量声明中的逗号不是使用产生值的运算符
    【解决方案4】:

    扮演魔鬼代言人,把问题颠倒过来可能是合理的:

    总是使用分号终止符是一种好习惯吗?

    几点:

    • 用逗号替换大多数分号会立即使大多数 C 和 C++ 代码的结构更清晰,并且会消除一些常见错误。
    • 这更像是函数式编程而不是命令式编程。
    • Javascript 的“自动分号插入”是其有争议的语法特性之一。

    这种做法是否会增加“常见错误”尚不清楚,因为没有人这样做。

    当然,如果你这样做了,你可能会惹恼你的程序员同行,并成为 SO 的贱民。

    编辑:参见AndreyT's 2009 年对Uses of C comma operator 的出色回答。并且Joel 2008 还谈到了 C#/C/C++ 中的两个并行语法类别。

    举个简单的例子,while (foo) a, b, c; 的结构很清楚,但while (foo) a; b; c; 在没有缩进或大括号或两者兼有的情况下会产生误导。

    编辑#2:正如AndreyT 所说:

    [The] C 语言(以及 C++)在历史上是两种完全不同的编程风格的混合体,可以称为“语句编程”和“表达式编程”。

    但他断言“在实践中语句编程产生更多更具可读性的代码”[强调添加]显然是错误。以他为例,您认为以下两行中哪一行更具可读性?

    a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;
    a = rand(); ++a; b = rand(); c = a + b / 2; if (a < c - 5) d = a; else d = b; 
    

    答案:它们不可读。正是 空白 赋予了可读性——Python 万岁!第一个更短。但是分号版本确实有更多的黑色空间绿色空间,如果你有一个Hazeltine终端——这可能是这里真正的问题吗?

    【讨论】:

    • 考虑相反的位置总是很有趣,但这表示我希望看到更多证据证明你的观点,因为在大多数情况下, , 和 ; 之间的语义差异很小。除了 for (;;) a, b, c 与 for (;;) a;乙; c;
    • @Bowie:我没有任何证据,正如我在回答中承认的那样。但是你的两个例子很好:第一个 for (;;) a, b, c; 很清楚,但第二个 for (;;) a; b; c; 在没有缩进或大括号或两者的情况下具有误导性。
    • 现在Occupy Wall Street 运动似乎正在消退,也许我们可以将注意力转回到逗号这个至关重要的问题上?
    【解决方案5】:

    每个人都说它经常用在 for 循环中,这是真的。但是,我发现它在 for 循环的条件语句中更有用。例如:

    for (int x; x=get_x(), x!=sentinel; )
    {
        // use x
    }
    

    在不使用逗号运算符的情况下重写它至少需要做一些我不太习惯的事情中的一件,例如在使用它的范围之外声明 x,或者在第一次调用 get_x() 时使用特殊大小写。

    我还在绘制如何将它与 C++11 constexpr 函数一起使用,因为我猜它们只能由单个语句组成。

    【讨论】:

    • 你可以写(x=get_x())!=sentinel,但我觉得你的方式更清楚。
    【解决方案6】:

    我认为唯一常见的例子是 for 循环:

    for (int i = 0, j = 3; i < 10 ; ++i, ++j)
    

    c-faq中提到的:

    有时,您会发现自己处于 C 期望 单一的表达,但你有两件事要说。最多 常见的(实际上是唯一常见的)示例在 for 循环中, 特别是第一个和第三个控制表达式。

    【讨论】:

    • 在第一部分(在您的示例中)它不是逗号运算符。
    • @MByD:这是一个逗号,但它没有使用逗号运算符。声明多个变量时允许使用逗号。右边的部分 逗号运算符。
    • 我的只是一个评论,因为引文是关于第一部分和第三部分。可能,鉴于该引用,最好将 ij 的声明留在 for 之外(因为在 C 中是必要的),只在里面留下双重初始化。
    【解决方案7】:

    我能想到的唯一合理用途是在for 构造中

    for (int count=0, bit=1; count<10; count=count+1, bit=bit<<1)
    {
        ...
    }
    

    因为它允许同时增加多个变量,仍然保持for 构造结构(对于受过训练的眼睛来说易于阅读和理解)。

    在其他情况下,我同意这是一种糟糕的 hack...

    【讨论】:

    • count = count + 1? bit = bit &lt;&lt; 1?
    • @SethCarnegie:是的。最初的版本是count++,bit&lt;&lt;=1,但我认为这样更易读。
    【解决方案8】:

    我还使用逗号运算符将相关操作粘合在一起:

    void superclass::insert(item i) {
      add(i), numInQ++, numLeft--;
    }
    

    【讨论】:

    • 如果在您的示例中 , 将被 ; 替换,会有什么不同吗?它看起来略有不同(虽然非常轻微),但在这种情况下,“粘合在一起”是什么意思?
    • 在这里不会有什么不同。但会在这里:while (1) ++i, ++j。粘合在一起意味着对这些语句进行逻辑分组,以便清楚地看出这些操作是紧密相关的。
    【解决方案9】:

    逗号运算符对于将序列放在无法插入代码块的地方很有用。正如所指出的,这在编写紧凑且可读的循环时很方便。此外,它在宏定义中很有用。以下宏会增加警告的数量,如果设置了布尔变量,也会显示警告。

    #define WARN if (++nwarnings, show_warnings) std::cerr
    

    这样你就可以写(例子1):

    if (warning_condition)
        WARN << "some warning message.\n";
    

    逗号运算符实际上是一个糟糕的 mans lambda 函数。

    【讨论】:

    • 是的,它可能是@kaizer.se,但我已经开始重视实用主义并且宏可以完成工作。
    【解决方案10】:

    虽然在 C++11 获得批准几个月后发布,但我在这里看不到任何与 constexpr 函数有关的答案。 This answer 对一个不完全相关的问题引用了关于逗号运算符及其在常量表达式中的有用性的讨论,其中特别提到了新的 constexpr 关键字。

    虽然 C++14 确实放宽了对 constexpr 函数的一些限制,但值得注意的是,逗号运算符可以在 constexpr 函数中授予您可预测的有序操作,例如(来自上述讨论) :

    template<typename T>
    constexpr T my_array<T>::at(size_type n)
    {
        return (n < size() || throw "n too large"), (*this)[n];
    }
    

    甚至是这样的:

    constexpr MyConstexprObject& operator+=(int value)
    {
        return (m_value += value), *this;
    }
    

    这是否有用完全取决于实现,但这只是逗号运算符如何应用于constexpr 函数的两个简单示例。

    【讨论】:

      猜你喜欢
      • 2020-08-19
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      • 2019-03-06
      • 2013-03-30
      • 2015-09-02
      • 2012-04-12
      相关资源
      最近更新 更多