2010-11-10 196 views
6

请看,我没有得到的是,为什么像下面这样的程序是合法的?在C++中没有副作用的表达式

int main() 
{ 
    static const int i = 0; 
    i <i> i; 
} 

我的意思,当然,没有人实际上有有没有副作用在其中表达的任何现有计划,因为那将是非常没有意义的,它将使解析&编译语言要容易得多。那么为什么不禁止他们呢?这种语法实际上从这种语法中获得了什么好处?

另一个例子是这样的:

int main() { 
    static const int i = 0; 
    int x = (i); 
} 

什么是这样陈述的实际好处?

而最令人头疼的解析。有没有人在其他函数中声明函数?我的意思是,我们摆脱了隐式函数声明等事情。为什么不只是摆脱他们的C++ 0x?

回答

4

它将使解析&编译 语言更容易

我怎么没看到。为什么分析和编译i <i> i更容易,如果你需要需要来发布诊断,而不是解析它,如果你被允许做任何你该死的任务,请确保发出的代码没有副作用?

Java编译器禁止无法访问的代码(与无效的代码相反),这对程序员来说是一种混合的祝福,并且需要编译器的额外工作,而不是C++编译器实际需要做的事情(基本块依赖性分析)。 C++应该禁止无法访问的代码?可能不会。尽管C++编译器确实做了足够的优化来识别无法访问的基本块,但在某些情况下,它们可能做得太多。如果foo是一个错误的编译时常量,if (foo) { ...}应该是一个非法的不可达块吗?如果它不是一个编译时常量,但是优化器已经计算出如何计算该值,如果它是合法的并且编译器必须认识到它将它移除的原因是特定于实现的,以免给出错误?更特殊的情况。

没有人真正有任何电流 程序,有没有 副作用他们

载荷表达式。例如,如果NDEBUG为真,那么assert将扩展为无效表达式,但不起作用。所以在编译器中需要更多特殊情况来允许一些无用的表达式,但不允许使用其他表达式。 (a)编译器最终会抛出诸如if (foo) assert(bar);之类的警告,并且(b)这样的代码在发布时是合法的,但在调试时不合法,只是令人困惑:

assert(foo) // oops, forgot the semi-colon 
foo.bar(); 

之类的东西最让人头疼的解析

,这就是为什么它被称为 “令人烦恼”。这是一个向后兼容的问题。如果C++现在改变了那些烦人的分析的含义,现有代码的含义就会改变。正如你指出的那样,现有的代码并不多,但C++委员会在向后兼容性方面采取了相当强硬的措施。如果您想要每五分钟更改一次的语言,请使用Perl ;-)

无论如何,现在已经太晚了。即使我们对C++ 0x委员会错过了一些很好的见解,为什么某些特性应该被删除或者不兼容地改变,除非FCD明确错误,否则它们不会破坏FCD中的任何内容。

请注意,对于您的所有建议,任何编译器都可能为它们发出警告(实际上,我不明白您的问题与第二个示例有什么关系,但对于无用的表达式和对函数体中令人厌烦的解析来说肯定是这样) 。如果你是对的,没有人故意这样做,警告不会造成任何伤害。如果你错误地认为没有人这么做,你所说的删除它们的情况是不正确的。流行编译器中的警告可能会为删除功能铺平道路,特别是因为标准主要由编译器编写者撰写。事实上,我们并不总是得到这些事情的警告,这对我来说意味着它比你想象的还要多。

8

可能是因为禁止会使规范更复杂,这会使编译器更复杂。

+0

除了如果编译器有两种方法可以解析语句,但其中一种是非法的,那么它知道它必须是另一种,这很简单,而如果标准允许这两种方式,那么编译器必须决定哪一个,哪一个简单得多。那里的可行性程序越不可靠,您需要决定哪些程序的逻辑就越少。 – Puppy 2010-11-10 12:43:10

+0

虽然这并非总是如此,因为编译器需要检测某些无效表达式并报告适当的错误消息。在任何情况下,如你所暗示的,它肯定不能认为代码是有效的。 – 2010-11-10 16:02:36

+0

+1表示智能(和有效)参数。 :-) – 2011-01-04 22:09:43

0

为什么不应该将其视为一种特殊情况?此外,虽然上述情况很容易发现,但人们可以想象更加复杂的程序,其中不容易识别没有副作用。

0

作为C++标准的一个迭代,C++ 0x必须向后兼容。没有人可以断言你写的这些陈述并不存在于美国国家航空航天局或国防部编写的一些关键软件中。

反正关于你的第一个例子中,解析器不能断言i是一个静态常量表达式,而i <i> i是一个无用的表达 - 例如如果i是模板类型,则i <i> i是“无效变量声明”,而不是“无用计算”,但仍不是分析错误。

+1

没有人说NASA或DoD必须升级到C++ 0x。如果他们喜欢它,那么他们可以保留C++ 03。旧的编译器版本不会奇迹般地消失。 – Puppy 2010-11-10 12:51:19

0

也许操作员被超载有副作用,如cout<<i;这就是为什么他们现在不能被删除的原因。另一方面,C#禁止非赋值语句或方法调用表达式作为语句,我相信这是一件好事,因为它使代码更清晰,语义更正确。但是C#有机会从C++没有的最开始就禁止它。

3
  • 有时候会将无用的语句放入程序并编译它以确保它们是合法的 - 例如,涉及的类型可以被解析/匹配等。

  • 特别是在生成的代码(宏以及更复杂的外部机制,策略或类型可能会在某些非操作情况下引入无意义的扩展的模板)不可编译的案例,以避免让事情变得更简单

  • 可能会有一些临时注释的代码去除变量的有意义的用法,但是可能很痛苦的是必须类似地识别和评论其他地方未使用的所有变量。

  • 虽然在您的示例中显示的变量是“int”,紧挨在无意义的用法之上,但实际上类型可能更加复杂(例如,操作符<())以及操作是否具有副作用甚至可能是编译器所不知道的(例如,外联函数),所以任何好处都局限于更简单的情况。

  • C++需要一个很好的理由来反转(和保留C)兼容性。

0

没有副作用的表达式可能比您在模板化和宏代码中的想法更频繁。如果你曾经声明std::vector<int>,你已经实例化模板代码,没有副作用。 std::vector在释放自身时必须销毁所有元素,以防存储类型为T的类。这需要在某种程度上类似于ptr->~T();来调用析构函数。 int虽然没有析构函数,所以调用没有副作用,并且会被优化器完全删除。它也可能在一个循环内,然后整个循环没有副作用,所以整个循环被优化器删除。

所以,如果你不允许的,无副作用的表达,std::vector<int>是行不通的,换一个。

另一种常见情况是assert(a == b)。在发布版本中,您希望这些断言消失 - 但不能将其重新定义为空宏,否则像if (x) assert(a == b);这样的语句会突然将下一个语句放入if语句中 - 这是一种灾难!在这种情况下,assert(x)可以重新定义为((void)0),这是一个没有副作用的声明。现在,if语句在发布版本中也能正确运行 - 它只是无所作为。

这些只是两个常见的情况。还有很多你可能不知道的。所以,虽然没有副作用的表达看起来多余,但它们实际上在功能上很重要。优化器将完全移除它们,所以也不会影响性能。

相关问题