43

Why is

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

valid C++ but not valid C?

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
code_dragon
  • 732
  • 5
  • 12
  • 3
    Neither compiler will state "doesn't work"! –  Jan 11 '18 at 09:14
  • 1
    This question is somewhat meaningless because there' re different implementation of C and C++ compilers from different company throughout years. – Gordon Jan 11 '18 at 09:14
  • 29
    Why should different languages behave identical? – Gerhardh Jan 11 '18 at 09:17
  • 28
    This is a good question, which happens to be worded poorly. Why did Stroustrup decide to make the preincrement operator yield an lvalue in C++ when it did not yield an lvalue in C? I seem to remember seeing the answer on this site somewhere, but I can't remember what it was. – Brian Bi Jan 11 '18 at 09:33
  • 18
    I also know that C and C++ are different languages, but the current C++ standard still says in [intro.scope] *C++ is a general purpose programming language based on the C programming language*. In addition the C++ standard library **explicitely** includes the C standard library (20.2 The C standard library [library.c]). And even if the question is not so good, the answer to it involves to understand what is an lvalue. It is really a pity than any question about subtle differences receives such a flame war. – Serge Ballesta Jan 11 '18 at 09:39
  • @Gordon Questions tagged C++ or C assume the current language standard, unless the OP says otherwise. That means ISO 14882:2017 and ISO 9899:2011. The behavior of older compilers and non-standard compilers is irrelevant. – Lundin Jan 11 '18 at 09:50
  • 5
    And indeed I see nothing wrong with this question. Compiler messages from the C compiler would have been helpful, but that's about it. The code itself might not make sense in a real program, but that's what the [tag:language-lawyer] tag is for. – Lundin Jan 11 '18 at 09:56
  • 14
    I have opened a [discussion](https://meta.stackoverflow.com/q/361795/3545273) on meta about the heavy downvoting on this question – Serge Ballesta Jan 11 '18 at 10:34
  • 1
    Irrespective, it is a bad idea to write code like this in *any* language. Unless you are deliberately trying to obscure the code's meaning. (And ... that's a bad idea too.) – Stephen C Jan 12 '18 at 07:25
  • Why questions are always a bit problematic unless one motivates somehow why it should not be so? So, I would like to know more about why C and C++ should behave identically in this regard? – NoDataDumpNoContribution Jan 12 '18 at 08:43
  • Also see [The difference between C and C++ regarding the ++ operator](https://stackoverflow.com/a/25656964/1708801) – Shafik Yaghmour Jan 15 '18 at 06:04
  • 1
    Voting to reopen. This question should be the duplicate source because it has better answers. We don't pedantically close new Qs as dupes of old Qs. We close Qs with less or not-so-great answers as dupes of Qs with better answers. – iBug Mar 13 '18 at 02:36

4 Answers4

45

C and C++ say different things about the result of prefix ++. In C++:

[expr.pre.incr]

The operand of prefix ++ is modified by adding 1. The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type other than cv bool, or a pointer to a completely-defined object type. The result is the updated operand; it is an lvalue, and it is a bit-field if the operand is a bit-field. The expression ++x is equivalent to x+=1.

So ++ can be applied on the result again, because the result is basically just the object being incremented and is an lvalue. In C however:

6.5.3 Unary operators

The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue.

The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation.

The result is not an lvalue; it's just the pure value of the incrementation. So you can't apply any operator that requires an lvalue on it, including ++.

If you are ever told the C++ and C are superset or subset of each other, know that it is not the case. There are many differences that make that assertion false.

Community
  • 1
  • 1
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 3
    I'm not the downvoter (nor am I a C or C++ dev), but one possible reason to downvote is that this only comes at "why?" from one angle - how do the specifications differ, and what is the effect of that? - and misses what is *potentially* a more interesting and illuminating one: what *motivates* those spec differences? Are there differences in design principles between C and C++ that this spec difference naturally follows from? Or features in C++ that render `++++x` a useful thing to allow when it wouldn't be in C? That conception of "why?" isn't addressed in either answer, yet. – Mark Amery Jan 11 '18 at 10:53
  • 4
    @MarkAmery - I'd have loved to provide it. But that information I, as of yet, was unable to recover. It's an historical divergence that I don't believe was often discussed. – StoryTeller - Unslander Monica Jan 11 '18 at 10:56
14

In C, it's always been that way. Possibly because pre-incremented ++ can be optimised to a single machine code instruction on many CPUs, including ones from the 1970s which was when the ++ concept developed.

In C++ though there's the symmetry with operator overloading to consider. To match C, the canonical pre-increment ++ would need to return const &, unless you had different behaviour for user-defined and built-in types (which would be a smell). Restricting the return to const & is a contrivance. So the return of ++ gets relaxed from the C rules, at the expense of increased compiler complexity in order to exploit any CPU optimisations for built-in types.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • +1. But I think the operator overloading argument is a bit circular. We have canon because *"we should do as the little ints do"*. But why the ints do it then? – StoryTeller - Unslander Monica Jan 11 '18 at 11:03
  • @StoryTeller: I think this is a case where `int` saw `class` coming: if we're going to apply `++` to objects of class type without copying them, we might as well do it for primitive types as well. `?:` is the same way. – Davis Herring Jan 11 '18 at 14:34
  • @DavisHerring - Perhaps. It's also possible it was to facilitate other idioms. But I haven't a clue as to which. I still agree with Bathsheba's assessment, don't get me wrong. – StoryTeller - Unslander Monica Jan 11 '18 at 14:37
  • @DavisHerring: Overloading `?:` is pretty nigh on impossible in the current language framework due to only two of the three expressions making up the operator ever being evaluated. – Bathsheba Jan 11 '18 at 14:39
  • @Bathsheba: Of course; I meant `(flag ? obj : other).setFoo(1);`, which can work only if `?:` can return an lvalue, again unlike C. Sorry I was unclear. – Davis Herring Jan 11 '18 at 14:43
  • @DavisHerring: I did overlook one thing though; when you overload `&&` and `||` you lose the short-circuiting property, although I feel that doing something similar would be a step too far for an overloaded `?:` – Bathsheba Jan 12 '18 at 08:06
  • @Bathsheba: Quite. `&&` _may_ leave one operand unevaluated. `?:` _always_ does. – Davis Herring Jan 12 '18 at 08:26
4

I assume you understand why it's fine in C++ so I'm not going to elaborate on that.

For whatever it's worth, here's my test result:

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

Regarding CppReference:

Non-lvalue object expressions

Colloquially known as rvalues, non-lvalue object expressions are the expressions of object types that do not designate objects, but rather values that have no object identity or storage location. The address of a non-lvalue object expression cannot be taken.

The following expressions are non-lvalue object expressions:

  • all operators not specified to return lvalues, including

    • increment and decrement operators (note: pre- forms are lvalues in C++)

And Section 6.5.3.1 from n1570:

The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation.

So in C, the result of prefix increment and prefix decrement operators are not required to be lvalue, thus not incrementable again. In fact, such word can be understood as "required to be rvalue".

iBug
  • 35,554
  • 7
  • 89
  • 134
2

The other answers explain the way that the standards diverge in what they require. This answer provides a motivating example in the area of difference.

In C++, you can have a function like int& foo(int&);, which has no analog in C. It is useful (and not onerous) for C++ to have the option of foo(foo(x));.

Imagine for a moment that operations on basic types were defined somewhere, e.g. int& operator++(int&);. ++++x itself is not a motivating example, but it fits the pattern of foo above.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • You could argue that a function like `int* foo(int*);` would be an analog in C, called as `*foo(foo(&x));`. It's essentially and functionally the same. The C++ way (with references) is just syntactic sugar. – Frxstrem Jan 13 '18 at 15:30
  • @Frxstrem Not quite, because you can't supply or return `nullptr` from the reference version – Caleth Jan 13 '18 at 17:06