【问题标题】:Should a diagnostic be emmited for discarded value expressions that do not have side effects?是否应该对没有副作用的丢弃值表达式进行诊断?
【发布时间】:2014-09-19 17:18:18
【问题描述】:

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

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)

【问题讨论】:

  • 我希望收到未使用的警告。
  • @chris 这是我从 clang 中得到的。来自 g++ 的 -Wall.
  • @JosephMansfield,确实如此。它甚至是三个警告,所以它真的很吸引你的眼球。
  • 然而,Visual Studio 不会抱怨,即使是 /Wall
  • @NikosAthanasiou 这是关于未使用表达式结果的警告,而不是变量。

标签: c++ debugging warnings comma-operator


【解决方案1】:

在 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) 期间,p1p2 的计算将导致未使用:

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

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

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

【讨论】:

  • “编译器会发出警告” - 你的意思是,“如果你启用合适的警告,一些编译器会发出警告”。不需要对此进行诊断,显然 OPs 编译器不需要(至少在默认设置下)。
【解决方案2】:

这是完全有效的代码,因此编译器不必发出诊断,尽管在这种情况下进行诊断会有所帮助。这是许多开发人员更喜欢clang 的原因之一,因为他们倾向于使用above what is required when it comes to diagnostic

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

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

  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 )

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

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

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

和一个丢弃的值表达式5部分介绍:

在某些情况下,表达式仅出于其副作用而出现。 这样的表达式称为弃值表达式。

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

bool func()
{
    //...
}

并将您的代码更改为:

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

clanggcc 都不会提供警告,因为大概func 会执行一些您关心的副作用

【讨论】:

  • 但是您会接受编译器可能会超越并在某种程度上发出警告吗?
  • @david.pfx 当然,我在回答中添加了一个小的说明。
  • 你能澄清一下:S5/11 an expression only appears for its side-effects ... is called a discarded-value expression. 如果没有副作用,那又是什么?
  • @david.pfx 添加了更多详细信息。
  • 另见stackoverflow.com/questions/24912609/…。如果您强制转换为 void,MSVC 显然会发出警告。
猜你喜欢
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多