14

我想答案是“不”,但从编译器的角度来看,我不明白为什么。

我编写了一个非常简单的代码,它严重影响了编译器诊断(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是重言式,这意味着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;
}

现在编译器给了我完全不同的警告。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;
}

然后突然clang意识到可能有一些可疑的东西a

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

但是a之前没有问题……由于某些原因,clang 无法在这种情况下发现重言式。同样,这可能只是因为这些不再是完整的陈述。

问题是:

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

编辑和评论:

  • 我收到了几个(正确的)评论,这些代码一点也不简单。这是真的,但关键是编译器在遇到初始化程序中的逗号分隔语句时会误诊。这是一件坏事。我使我的代码更完整以避免“你尝试过这种语法......”的评论。可以编写一个更现实和人类可读的问题版本,它会显示错误的诊断,但我认为这个版本显示了更多信息并且更完整。
  • 在编译器折磨测试套件中,这将被认为是非常易于理解和可读的,它们做得更糟:) 我们需要这样的代码来测试和评估编译器。这在生产代码中看起来并不漂亮,但这不是重点。
4

1 回答 1

1

5 表达式

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

5.18 逗号运算符 [expr.comma]

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

在我看来,你的说法没有任何问题。

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

于 2013-05-28T18:58:16.430 回答