我猜答案是"不",但从编译器的角度来看,我不明白为什么。
我做了一个非常简单的代码,它使编译器诊断非常糟糕(clang和gcc),但我希望在报告错误诊断之前确认代码没有格式化。我应该指出这些是而不是编译器错误,在所有情况下输出都是正确的,但我对警告有疑问。
考虑以下代码:
#include <iostream>
int main(){
int b,a;
b = 3;
b == 3 ? a = 1 : b = 2;
b == 2 ? a = 2 : b = 1;
a = a;
std::cerr << a << std::endl;
}
a
的赋值是一个重言式,这意味着无论b
如何,a
都将在两个三元语句之后初始化。GCC对这段代码非常满意。Clang稍微聪明一点,发现了一些愚蠢的东西(warning: explicitly assigning a variable of type 'int' to itself [-Wself-assign]
),但没什么大不了的。
现在是相同的东西(至少在语义上),但语法更短:
#include <iostream>
int main(){
int b,a = (b=3,
b == 3 ? a = 1 : b = 2,
b == 2 ? a = 2 : b = 1,
a);
std::cerr << a << std::endl;
}
现在编译器给我完全不同的警告。Clang不再报告任何奇怪的东西(这可能是正确的,因为括号的优先级)。GCC有点吓人,它说:
test.cpp: In function ‘int main()’:
test.cpp:7:15: warning: operation on ‘a’ may be undefined [-Wsequence-point]
但这是真的吗?序列点警告给了我一个提示,即逗号分隔语句在实践中不以相同的方式处理,但我不知道它们是否应该。
更奇怪的是,把代码改成:#include <iostream>
int main(){
int b,a = (b=3,
b == 3 ? a = 1 : b = 2,
b == 2 ? a = 2 : b = 1,
a+0); // <- i just changed this line
std::cerr << a << std::endl;
}
,然后突然意识到a
可能有问题:
test.cpp:7:14: warning: variable 'a' is uninitialized when used within its own initialization [-Wuninitialized]
a+0);
^
但是a
在…之前没有问题。由于某些原因,clang在这种情况下无法发现同义反复。同样,这可能只是因为这些不再是完整的陈述。
问题是:
- 此代码有效且定义良好(在所有版本中)吗?
- 如何处理逗号分隔语句的列表?它应该与带有显式语句的第一个版本的代码不同吗?
- GCC是否有权报告未定义的行为和序列点问题?(在这种情况下clang缺少一些重要的诊断)我知道它说可能,但仍然…
- 报告
a
在最后一种情况下可能未初始化是正确的吗?(那么它应该具有与前一个病例相同的诊断)
编辑和注释:
- 我得到了几个(正确的)评论,这段代码是什么都不简单。这是对的,但问题是,当编译器在初始化器中遇到逗号分隔的语句时,它们会错误诊断。这是一件坏事。我让我的代码更完整,以避免"你试过这个语法…"评论。可以编写一个更现实和人类可读的问题版本,它会显示错误的诊断,但我认为这个版本显示了更多的信息,更完整。
- 在编译器折磨测试套件中,这将被认为是非常可理解和可读的,他们做得更糟:)我们需要这样的代码来测试和评估编译器。这在产品代码中看起来并不漂亮,但这不是这里的重点。
<5表达式/strong>
10在某些上下文中,表达式仅因其副作用而出现。这样的表达式称为丢弃值表达式。表达式被求值,其值被丢弃
5.18逗号操作符(expr.comma)
用逗号分隔的一对表达式从左到右求值;左边的表达式是一个丢弃值表达式(第5条)每一个与左表达式相关的值计算和副作用在每个值计算和副作用关联之前进行排序用正确的表达。结果的类型和值为右操作数的类型和值;结果是相同的值类别作为其右操作数,如果其为右操作数,则为位域是一个全局值和位域。
听起来你的话好像没有什么问题。
仔细观察g++警告,可能是未定义的,这告诉我解析器不够聪明,无法看到a=1
被保证求值。