【问题标题】:Why are multiple pre-increments allowed in C++ but not in C? [duplicate]为什么在 C++ 中允许多个预增量但在 C 中不允许? [复制]
【发布时间】:2018-06-20 13:28:29
【问题描述】:

为什么

int main()
{
    int i = 0;
    ++++i;
}

有效的 C++ 但无效的 C?

【问题讨论】:

  • 为什么不同的语言应该表现相同?
  • 这是个好问题,只是措辞不当。为什么 Stroustrup 决定让预增量运算符在 C++ 中产生左值,而它在 C 中没有产生左值?我似乎记得在这个网站的某个地方看到过答案,但我不记得是什么了。
  • 我也知道C和C++是不同的语言,但是目前的C++标准在[intro.scope]中还是说C++是基于C编程语言的通用编程语言我>。此外,C++ 标准库明确 包括 C 标准库(20.2 C 标准库 [library.c])。即使这个问题不太好,它的答案也涉及理解什么是左值。真的很可惜,比任何关于细微差异的问题都受到如此激烈的战争。
  • 事实上,我认为这个问题没有任何问题。来自 C 编译器的编译器消息会有所帮助,但仅此而已。代码本身在实际程序中可能没有意义,但这就是language-lawyer 标签的用途。
  • 我在 meta 上打开了一个 discussion,关于这个问题的大量反对意见

标签: c++ c language-lawyer


【解决方案1】:

C 和 C++ 对前缀 ++ 的结果有不同的说法。在 C++ 中:

[expr.pre.incr]

前缀++的操作数加1。 操作数应为 一个可修改的左值。操作数的类型应为算术 cv bool 以外的类型,或指向完全定义对象的指针 类型。 结果是更新后的操作数;它是一个左值,它是一个 如果操作数是位域,则位域。表达式 ++x 是 相当于 x+=1。

所以 ++ 可以再次应用于结果,因为结果基本上只是被递增的对象并且是一个左值。然而,在 C 中:

6.5.3 Unary operators

前缀递增或递减运算符的操作数应具有原子、合格或不合格的实数或指针类型,应为可修改的左值。

前缀 ++ 运算符的操作数的值递增。 该 result 是递增后操作数的新值

结果不是左值;这只是增量的纯值。所以你不能应用任何需要左值的运算符,包括 ++。

如果您曾经被告知 C++ 和 C 是彼此的超集或子集,请知道事实并非如此。有许多不同之处使该断言为假。

【讨论】:

  • 我不是投反对票的人(我也不是 C 或 C++ 开发人员),但投反对票的一个可能原因是这只会出现在“为什么?”从一个角度 - 规格有何不同,这有什么影响? - 错过了可能更有趣和更有启发性的东西:是什么激发了这些规范差异? C 和 C++ 之间的设计原则是否存在这种规范差异自然产生的差异?或者在 C++ 中呈现 ++++x 的功能,当它不在 C 中时允许它是有用的东西?那个“为什么”的概念两个答案都没有解决。
  • @MarkAmery - 我很乐意提供它。但到目前为止,我还无法恢复这些信息。这是一个历史分歧,我不认为经常被讨论。
【解决方案2】:

在 C 中,一直都是这样。可能是因为预增的++ 可以优化为许多 CPU 上的单个机器代码指令,包括 1970 年代开发的++ 概念。

在 C++ 中,尽管要考虑运算符重载的对称性。为了匹配 C,规范的预增量 ++ 需要返回 const &,除非您对用户定义和内置类型有不同的行为(这将是一种气味)。限制返回 const & 是一种设计。因此,++ 的返回从 C 规则中得到了放松,但以增加编译器复杂性为代价,以便利用内置类型的任何 CPU 优化。

【讨论】:

  • +1。但我认为运算符重载参数有点循环。我们有佳能是因为“我们应该像小整数那样做”。但是为什么整数会这样做呢?
  • @StoryTeller:我认为这是int 看到class 出现的情况:如果我们要将++ 应用于类类型的对象而不复制它们,我们不妨对原始类型也这样做。 ?: 也是一样。
  • @DavisHerring - 也许吧。也有可能是为了方便其他成语。但我不知道是哪个。我还是同意芭丝谢芭的评价,不要误会。
  • @DavisHerring:重载?: 在当前语言框架中几乎是不可能的,因为构成运算符的三个表达式中只有两个被评估。
  • @Bathsheba:当然;我的意思是(flag ? obj : other).setFoo(1);,它只有在?: 可以返回一个左值时才能工作,这又不像C。对不起,我不清楚。
【解决方案3】:

我假设你明白为什么它在 C++ 中很好,所以我不打算详细说明。

不管它值多少钱,这是我的测试结果:

t.c:6:2: error: lvalue required as increment operand
  ++ ++c;
  ^

关于CppReference

非左值对象表达式

通俗地称为右值,非左值对象表达式是不指定对象的对象类型的表达式,而是没有对象标识或存储位置的值。不能取非左值对象表达式的地址。

以下表达式是非左值对象表达式:

  • 所有运算符未指定返回左值,包括

    • 递增和递减运算符(注意:前置形式在 C++ 中是左值)

以及来自 n1570 的第 6.5.3.1 节:

前缀 ++ 运算符的操作数的值递增。 结果是递增后操作数的新

所以在C语言中,前缀自增和前缀自减运算符的结果不需要是左值,因此不能再自增。其实这样的词可以理解为“必须是右值”。

【讨论】:

    【解决方案4】:

    其他答案解释了标准在要求方面的分歧方式。这个答案在差异领域提供了一个鼓舞人心的例子。

    在 C++ 中,您可以拥有像 int& foo(int&); 这样的函数,它在 C 中没有类似物。对于 C++ 来说,拥有 foo(foo(x)); 的选项很有用(而且不繁琐)。

    想象一下,对基本类型的操作是在某个地方定义的,例如int& operator++(int&);++++x 本身并不是一个激励的例子,但它符合上述foo 的模式。

    【讨论】:

    • 您可能会争辩说像int* foo(int*); 这样的函数在C 中是一个类似的函数,称为*foo(foo(&x));。它本质上和功能上是相同的。 C++ 方式(带有引用)只是语法糖。
    • @Frxstrem 不完全是,因为您无法从参考版本中提供或返回 nullptr
    猜你喜欢
    • 1970-01-01
    • 2011-07-17
    • 2015-12-02
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-15
    相关资源
    最近更新 更多