2

经过相当长的调试时间后,我发现我的代码中有一个错误,归结为这样的事情,我感到很愚蠢:

int main()
{
    double p1[] = { 1, 2, 3 };
    double p2[] = { 1, 2, 3 };
    int color = 1;
    bool some_condition = true;

    if (some_condition) (p1, p2, color);
}

表达式的(p1, p2, color)计算结果是它的最后一个操作数,但是编译器应该以某种方式保护我吗?(Visual Studio 什么也没说)

是的,你猜对了,我想调用一个绘图函数:Draw(p1, p2, color)

4

2 回答 2

4

在 C++ 中,表达式(p1, p2, color)强制编译器将括号内的逗号解释为顺序求值运算符。顺序求值运算符是一个二元运算符,它将第一个操作数求为void并丢弃结果,然后对第二个操作数求值并返回其值和类型。因此,表达式(p1, p2, color)将按以下方式计算:

  1. 首先p1被评估并丢弃,然后(p2, color)被评估并(p2, color)返回结果。
  2. 首先p2被评估并丢弃,然后color被评估并color返回结果。

因此声明:

if (some_condition) (p1, p2, color);

相当于:

if (some_condition) color;

一些编译器可能会发出警告,因为在计算表达式期间,(p1, p2, color)p1and的计算p2将导致未使用:

CLANG LIVE DEMO GCC LIVE DEMO (正如您已经提到的, Visual Studio根本不会发出警告。)

除了这些警告之外,代码是合法的 C++(即不违反 C++ 语法)。

现在,编译器是否应该保护你是有争议的。在我的拙见中,它应该保护你,因为这样的表达式虽然从 C++ 语法的角度来看是正确的,但可能会导致很难发现错误(例如,if表达式内的赋值情况)。

于 2014-07-28T13:46:20.703 回答
3

它是完全有效的代码,因此编译器不必发出诊断,尽管在这种情况下进行诊断会有所帮助。这是许多开发人员更喜欢的原因之一,clang因为他们倾向于在诊断方面超出要求

对于诊断消息的标准规则,我们可以转到草案 C++ 标准部分1.4 实施合规性,其中说(强调我的):

  1. 可诊断规则集由本国际标准中的所有句法和语义规则组成,但那些包含“不需要诊断”的明确表示法或被描述为导致“未定义行为”的规则除外。</p>

  2. 尽管该国际标准仅规定了对 C++ 实现的要求,但如果将这些要求表述为对程序、程序部分或程序执行的要求,则通常更容易理解。此类要求具有以下含义:

    • 如果程序不包含违反本国际标准规则的行为,则符合要求的实现应在其资源限制内接受并正确执行2该程序。

    • 如果程序包含违反任何可诊断规则或出现在本标准中描述为“有条件支持”的构造,而实现不支持该构造,则符合要求的实现应发出至少一个诊断消息。

    • 如果一个程序违反了不需要诊断的规则,则本国际标准对该程序的实现没有要求。

该程序没有违反句法或语义规则,因此不需要诊断。

我们有以下代码:

if (some_condition) (p1, p2, color);
                    ^  ^   ^
                    1  2   3

1是一个表达式语句,在此上下文中对于和if 语句有效。我们可以通过语法看到这一点:

if ( condition ) statement

和:

statement:
  attribute-specifier-seqopt expression-statement

和:

expression-statement:
  expressionopt;

和:

primary-expression:
  ( expression )

2和都是逗号运算符3,它将评估左操作数并丢弃该值,然后再次评估右操作数,这里没有任何无效。

那么5.18 逗号运算符部分说的是什么:

一对用逗号分隔的表达式从左到右计算;左边的表达式是丢弃的值表达式(第 5 条)。83

丢弃的值表达式在部分5中介绍:

在某些情况下,表达式只出现在它的副作用上。这样的表达式称为弃值表达式。

因此,由于左侧表达式的结果值被丢弃了,所以我们必须只关心副作用。在您的特定情况下,评估变量除了生成一个值因此警告之外没有其他影响,但是如果您在它们的位置使用了一个函数,例如:

bool func()
{
    //...
}

并将您的代码更改为:

if (some_condition) (func(), func(), func() );

clang不会也gcc不会提供警告,因为可能func会执行一些您关心的副作用。

于 2014-07-28T14:03:17.517 回答