逗号分隔的陈述是否被视为完整陈述?(和其他诊断问题)

Thi*_*aut 14 c++ gcc clang compiler-warnings

我猜答案是"不",但从编译器的角度来看,我不明白为什么.

我制作了一个非常简单的代码,它使得编译器诊断非常糟糕(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;
}
Run Code Online (Sandbox Code Playgroud)

赋值a是一个重言式,意思是a将在两个三元语句之后初始化,无论如何b.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;
}
Run Code Online (Sandbox Code Playgroud)

现在编译器给了我完全不同的警告.Clang不再报告任何奇怪的事情(由于括号优先,这可能是正确的).gcc有点吓人,并说:

test.cpp: In function ‘int main()’:
test.cpp:7:15: warning: operation on ‘a’ may be undefined [-Wsequence-point]
Run Code Online (Sandbox Code Playgroud)

但这是真的吗?这个序列点警告给了我一个暗示,在实践中不会以相同的方式处理昏迷分离的语句,但我不知道它们是否应该.

它变得更奇怪,将代码更改为:

#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;
}
Run Code Online (Sandbox Code Playgroud)

然后突然铿锵地意识到可能有些东西可疑a:

test.cpp:7:14: warning: variable 'a' is uninitialized when used within its own initialization [-Wuninitialized]
             a+0);
             ^
Run Code Online (Sandbox Code Playgroud)

a之前没有问题...由于某些原因,clang在这种情况下无法发现同义反复.同样,它可能只是因为那些不再是完整的陈述.

问题是:

  • 这段代码是否有效且定义明确(在所有版本中)?
  • 如何处理逗号分隔语句列表?它应该与具有显式语句的代码的第一个版本不同吗?
  • GCC是否有权报告未定义的行为和序列点问题?(在这种情况下,clang缺少一些重要的诊断)我知道它可能会,但仍然......
  • 是否有权报告a在最后一案中可能未初始化?(那么它应该对前一种情况有相同的诊断)

编辑和评论:

  • 我得到了几条(合法的)评论,这段代码简直就是这么简单.这是事实,但重点是编译器在初始化程序中遇到逗号分隔语句时会误诊断.这是件坏事.我让我的代码更完整,以避免"你试过这种语法......"评论.可以编写一个更加真实和人类可读的问题版本,这将显示错误的诊断,但我认为这个版本显示更多信息并且更完整.
  • 在编译器 - 酷刑测试套件中,这将被认为是非常容易理解和可读的,它们做得更糟:)我们需要这样的代码来测试和评估编译器.这在生产代码中看起来不太合适,但这不是重点.

Ste*_*all 1

5 表达式

10 在某些情况下,表达式仅因其副作用而出现。这样的表达式称为丢弃值表达式。计算表达式并丢弃其值

5.18 逗号运算符[expr.comma]

由逗号分隔的一对表达式从左到右计算;左侧表达式是丢弃值表达式(第 5 条)。83 与左侧表达式相关的每个值计算和副作用都在与右侧表达式相关的每个值计算和副作用之前排序。结果的类型和值是右操作数的类型和值;结果与其右操作数具有相同的值类别,并且如果其右操作数是左值和位字段,则结果是位字段。

我觉得你的说法没有什么问题。

更仔细地查看 g++ 警告,可能是未定义的,这告诉我解析器不够智能,无法看到a=1保证被评估。