经过相当长的调试时间,我发现代码中的一个错误,归结为以下内容,我觉得很傻:
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函数:Draw(p1, p2, color)
在C++中,表达式(p1, p2, color)
迫使编译器将括号内的逗号解释为顺序求值运算符。顺序求值运算符是一种二进制运算符,它将第一个操作数求值为void
并丢弃结果,然后求值第二个操作数并返回其值和类型。因此,表达式(p1, p2, color)
将以以下方式进行评估:
- 首先对
p1
进行评估并丢弃,然后对(p2, color)
进行评估并返回结果(p2, color)
- 首先对
p2
进行评估并丢弃,然后对color
进行评估并返回结果color
因此声明:
if (some_condition) (p1, p2, color);
相当于:
if (some_condition) color;
某些编译器可能会发出警告,因为在对表达式(p1, p2, color)
求值期间,对p1
和p2
的求值将导致未使用:
CLANG现场演示GCC现场演示(正如您已经提到的,Visual Studio不会发出任何警告。(
除了这些警告之外,代码是合法的C++(即,没有违反C++语法(。
现在,编译器是否应该保护您是有争议的。在我看来,它应该保护你,因为这样的表达式虽然从C++语法的角度来看是正确的,但可能会导致很难发现的错误(例如,if
表达式中的assingment(。
它是完全有效的代码,因此编译器不必发布诊断,尽管在这种情况下进行诊断会有所帮助。这也是许多开发人员更喜欢clang
的原因之一,因为在诊断方面,他们往往会超出要求。
关于诊断消息的标准规则,我们可以参考C++标准草案1.4
实现合规性,其中说(emphasis mine(:
可诊断规则集由所有句法和语义组成本国际标准中的规则,但包含"无需诊断"或被描述为导致"未定义的行为">
尽管本国际标准仅规定了C++如果它们被表述为对程序、程序部分的要求,或者程序的执行。此类要求具有以下含义:
如果程序没有违反本国际标准中的规则,则合格实施应在其资源限制内接受并正确执行该程序2。
如果程序包含违反任何可诊断规则或出现中描述的构造当实现不支持该构造时,该标准被视为"有条件支持",一致性实施应发出至少一条诊断消息
如果某个程序违反了不需要诊断的规则,则本国际标准对该程序的实现没有任何要求。
该程序不违反语法或语义规则,因此不需要进行诊断。
我们有以下代码:
if (some_condition) (p1, p2, color);
^ ^ ^
1 2 3
和: 和: 和: 那么 由逗号分隔的一对表达式从左到右进行求值;左边的表达式是一个被丢弃的值表达式(第5条(83 并且丢弃的值表达式包含在CCD_ 22部分中,该部分表示: 在某些情况下,表达式只会因其副作用而出现。这样的表达式称为丢弃值表达式。 因此,由于左手表达式的结果值被丢弃,因此我们必须只关心的副作用。在您的特定情况下,评估变量除了生成一个值之外没有其他影响,因此会发出警告,但如果您在其位置使用了一个函数,例如: 并将您的代码更改为:1
是表达式语句,在此上下文中对和if语句if ( condition ) statement
statement:
attribute-specifier-seqopt expression-statement
expression-statement:
expressionopt;
primary-expression:
( expression )
2
和3
都是逗号运算符,它将计算左操作数并丢弃该值,然后再次计算右操作数—此处没有无效值。5.18
Comma运算符部分说了什么:bool func()
{
//...
}
if (some_condition) (func(), func(), func() );
clang
和gcc
都不会提供警告,因为func
可能会产生您关心的一些副作用。