Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

lvalue assignment difference between C and C++

106 views
Skip to first unread message

Mut...@dastardlyhq.com

unread,
Feb 4, 2023, 10:30:40 AM2/4/23
to
Can anyone explain the "official" explanation from whichever standard as to
why the first assignment only compiles in C++ but not C:

#include <stdio.h>

int main()
{
int a = 0;
int b = 0;
int x = 0;
int y = 1;
int c = 123;

/* Compiles in C++, not C
(x == y ? a : b) = c;
*/

/* C version */
*(x == y ? &a : &b) = c;

printf("a = %d, b = %d\n",a,b);
return 0;
}

Just curious.

Richard Damon

unread,
Feb 4, 2023, 11:04:28 AM2/4/23
to
The key difference is that in C, the results of the conditional operator
is always an r-value, and thus not suitable for an assignment operator.

By using the address-of and deference operatiors you convert that
r-value into an l-value so can make the assignment.

In C++, because it has a "reference" type, if the last two terms of the
conditional operator are objects of a compatible type, then the type of
the result can be a reference to that type, an option not available in
C, and this allows its value to be an l-value.

Mut...@dastardlyhq.com

unread,
Feb 4, 2023, 11:59:39 AM2/4/23
to
Ok, that makes sense. Though I wonder - given the main C compilers used now
are also C++ compilers - why they don't just silently allow it in C? Would
there be any wierd or unfortunate side effects in C if it was?

Richard Damon

unread,
Feb 4, 2023, 1:02:48 PM2/4/23
to
Mostly because in C it is a Required Diagnostic, so to allow it they
need to at least issue a warning, so it can't be "silent" without
something to at least implicitly define you don't want conformance.

Also, basically it goes down to the differing type inference rules of
the two langauges, so it would need some explicit code injected into the
C parsing part of the compiler.

While I can't think of any specific "harm" that allowing it in C as an
extention would cause, since it is always a full error in C, I;m not
sure that adding it would add that much value as an extenuation since it
makes the code needlessly unportable, since the C version isn't that
more complicated.

Bonita Montero

unread,
Feb 4, 2023, 1:40:17 PM2/4/23
to
Am 04.02.2023 um 16:30 schrieb Mut...@dastardlyhq.com:

> (x == y ? a : b) = c;

I'm sometimes writing code like this and I love it.
If you use it regulary this becomes more readbale
than the if'd code.

Keith Thompson

unread,
Feb 4, 2023, 3:46:07 PM2/4/23
to
Mut...@dastardlyhq.com writes:
[...]
> Ok, that makes sense. Though I wonder - given the main C compilers used now
> are also C++ compilers - why they don't just silently allow it in C? Would
> there be any wierd or unfortunate side effects in C if it was?

No, most C compilers are not also C++ compilers.

gcc and g++, for example, are part of the same project, but the front
ends (where conditional expressions are handled) are separate.

For example, I've never seen a grammar that conditionally treats "class"
as a keyword depending on the language being processed, and I'd be
surprised to see a compiler that uses that approach.

A C compiler could support treating conditional expressions as lvalues
as an extension (which would be non-conforming if it didn't produce a
diagnostic message), and it might be implemented by borrowing code from
the corresponding C++ front end, but I wouldn't expect the same code to
handle conditional expressions in C and C++ compilers.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */

David Brown

unread,
Feb 5, 2023, 5:42:21 AM2/5/23
to
On 04/02/2023 17:59, Mut...@dastardlyhq.com wrote:

> Ok, that makes sense. Though I wonder - given the main C compilers used now
> are also C++ compilers - why they don't just silently allow it in C? Would
> there be any wierd or unfortunate side effects in C if it was?
>

Most C++ compilers have a closely related C compiler that is part of the
same overall compiler project, but the front ends for C and C++ are
usually separate programs. So major C++ compilers such as gcc, clang,
MSVC, ICC all support C within the same overall toolchain. But it is
normal that the front-end parsers are separate, even though the
middle-end optimisers and back-end code generators are shared.

On the other hand, the vast majority of C compilers are /not/ C++
compilers. With a bit of googling, you'll probably find a couple of
dozen C compilers available for PC's (Linux and/or Windows) freely
available, and only a few of them will also have C++ compilers. Go over
to the embedded world and you can find hundreds of C compilers, and only
perhaps 5% of them support C++.

If you look at hobby projects, one-man toolchains and niche compilers, I
have never heard of any that support C++, while a working C compiler is
something many people have written.

Paavo Helde

unread,
Feb 6, 2023, 2:11:43 AM2/6/23
to
This would basically introduce a limited notion of 'reference' in the C
language, which would make the language more complex to teach and learn.
That's the opposite to the goals C currently has.

And yes, having a simpler language in general means the programs written
in it need to be more complex. Here it just means a couple of extra
asterisks and ampersands, so not a big deal.


Keith Thompson

unread,
Feb 6, 2023, 4:02:01 AM2/6/23
to
Paavo Helde <ees...@osa.pri.ee> writes:
> 04.02.2023 18:59 Mut...@dastardlyhq.com kirjutas:
[...]
>> Ok, that makes sense. Though I wonder - given the main C compilers
>> used now
>> are also C++ compilers - why they don't just silently allow it in C? Would
>> there be any wierd or unfortunate side effects in C if it was?
>
> This would basically introduce a limited notion of 'reference' in the
> C language, which would make the language more complex to teach and
> learn. That's the opposite to the goals C currently has.
>
> And yes, having a simpler language in general means the programs
> written in it need to be more complex. Here it just means a couple of
> extra asterisks and ampersands, so not a big deal.

I agree it's not a big deal, and I've already commented on the idea
that most C compilers are also C++ compilers (they aren't).

But I don't think that allowing a conditional expression to be an
lvalue would require introducing references into the language.

An lvalue is defined as "an expression that potentially designated
an object" ("potentially" because *ptr is still an lvalue even if
ptr==NULL). This would just mean that `x ? y : z` would designate an
object, and the identify of that object depends on the value of `x`.
It's not all that much different from `a[i]`, where the identify
of the object it designates depends on the values of `a` and `i`
-- or `*p` for that matter.

If it were introduced in a future edition of the C standard, I don't
think it would cause many problems. (And the generated machine code
would probably use pointers anyway.) But I don't think there's enough
demand to justify it.

Mut...@dastardlyhq.com

unread,
Feb 6, 2023, 4:26:53 AM2/6/23
to
On Sun, 5 Feb 2023 11:42:04 +0100
David Brown <david...@hesbynett.no> wrote:
>On the other hand, the vast majority of C compilers are /not/ C++

Ok, I should have said most of the *common* C++ compilers are also C compilers.

>If you look at hobby projects, one-man toolchains and niche compilers, I
>have never heard of any that support C++, while a working C compiler is
>something many people have written.

I don't know if its true or not, but I read once that lexx and yacc are
quite capable of parsing C grammar and create the appropriate lex and parse
code but the C++ syntax is apparently beyond them because there are so many
token overloads and special cases. And that was IIRC before C++ 2011, never
mind now.

Öö Tiib

unread,
Feb 6, 2023, 6:27:51 AM2/6/23
to
Yes, C++ has always had tendency to make same row of tokens to mean very
different things depending on context. The lookup rules have always required
to find things (the above-mentioned context) from whole class as if already
parsed while parsing that class. The "export template" of C++98 was
so tricky that no main stream compiler was capable to implement it and so it
was removed by C++11.

Richard Damon

unread,
Feb 6, 2023, 7:42:35 AM2/6/23
to
I think the limitation is that to parse:

int a, b, c, d;
...
(a ? b : c) = d;

The "value" used from b, for parsing the expression isn't the integral
value of b, but something like a int& that will convert to the value if
needed. The whole design phylosophy of C is that while parse the
statement, you can evalutate the pieces largly ignoring remote context.
The remembering that you have an l-value never has to survive going
through an "expression" but always (currently) used just by the
immediate operator that consumes it.

To implement this, b's l-valueness (if c is also an l-value) through the
conditional operator to see how that is being used. Since conditionals
can be nested, this requires a somewhat arbitrary level of delay.

C++ get around that by b evaluating not to the value of b, but a
reference to b, that switches to the value if used as an r-value or
stays an l-value if used as an assignment target.

Thus the ability comes straight out of having a "reference" type.

David Brown

unread,
Feb 6, 2023, 8:14:06 AM2/6/23
to
On 06/02/2023 10:26, Mut...@dastardlyhq.com wrote:
> On Sun, 5 Feb 2023 11:42:04 +0100
> David Brown <david...@hesbynett.no> wrote:
>> On the other hand, the vast majority of C compilers are /not/ C++
>
> Ok, I should have said most of the *common* C++ compilers are also C compilers.
>

No, that would still be wrong. You should have said - as I wrote - that
most C++ compilers have a C compiler as part of their toolchain packages.

I don't know of any C++ compiler, common or not, that does not have an
associated C compiler. (The nearest, perhaps, would be MSVC - there you
have a modern C++ compiler combined with an outdated and non-conforming
C compiler.)

Mut...@dastardlyhq.com

unread,
Feb 6, 2023, 10:49:58 AM2/6/23
to
That would have been very useful. Having to put templated code in a header
file and constantly recompile it its a right PITA.

I can see the problem though - it would need a update of object file formats
which is probably non trivial.

Mut...@dastardlyhq.com

unread,
Feb 6, 2023, 10:51:08 AM2/6/23
to
On Mon, 6 Feb 2023 14:13:51 +0100
David Brown <david...@hesbynett.no> wrote:
>On 06/02/2023 10:26, Mut...@dastardlyhq.com wrote:
>> On Sun, 5 Feb 2023 11:42:04 +0100
>> David Brown <david...@hesbynett.no> wrote:
>>> On the other hand, the vast majority of C compilers are /not/ C++
>>
>> Ok, I should have said most of the *common* C++ compilers are also C
>compilers.
>>
>
>No, that would still be wrong. You should have said - as I wrote - that
>most C++ compilers have a C compiler as part of their toolchain packages.

You're splitting hairs. "Compiler" for most people is the entire toolchain,
not one particular part of it.

David Brown

unread,
Feb 6, 2023, 2:09:04 PM2/6/23
to
Those hairs are critical when you are considering "Why doesn't C++
support this if C does?" or "Why doesn't C support this if C++ does?".

I agree that for most people in normal use, the details of how
toolchains work is unimportant. You use the command "gcc" or "clang",
and it will compile C and C++, do the linking, handle assembly, and
often handle Objective-C, Fortran, and maybe other languages. But we
can be a bit more advanced in these groups. You wanted to know whether
it would be simple for a C compiler to support a particular feature that
is standard in C++ - one of the reasons why it is not immediately easy
to add as an extension to C is that the compilers have independent
frond-ends.

Keith Thompson

unread,
Feb 6, 2023, 3:47:52 PM2/6/23
to
Perhaps, but the reason you said that most C compilers are also C++
compilers (or however you phrased it) was to support the idea that
it would be trivial to implement rvalue conditional expressions in C.
The part of a C compiler that implements conditional expressions is,
in all cases I'm aware of, distinct from the part of a C++ compiler
that implements conditional expressions. Possibly some code could
be borrowed, but it's not just a matter of turning off a restriction.

The "entire toolchain" is not relevant to the point you raised.

Keith Thompson

unread,
Feb 6, 2023, 3:59:35 PM2/6/23
to
You make a good point that I hadn't thought of: that a conditional
expression would have to be an lvalue or not depending whether b and c
are both lvalues (and there would have to be tighter constraints on the
types of b and c in that case). And that would violate the (probably
not quite universal) rule that every C (sub-)expression is evaluated
without regard to its context.

The C++ standard does define this in terms of "lvalue references".

But I think this could be defined in a future edition of C without using
C++-style references, probably by saying something like:

A conditional expression is an lvalue if and only if the second
and third operands are lvalues of compatible types.

(it would probably require substantially more words than that).
I don't think it would be worth the effort, but I wouldn't complain
if the C committee and implementers decided to do it.

(I'll note that a parenthesized lvalue is an lvalue, but of course
that's much more trival than a conditional expression.)

Keith Thompson

unread,
Feb 6, 2023, 4:03:28 PM2/6/23
to
Mut...@dastardlyhq.com writes:
> On Sun, 5 Feb 2023 11:42:04 +0100
> David Brown <david...@hesbynett.no> wrote:
>>On the other hand, the vast majority of C compilers are /not/ C++
>
> Ok, I should have said most of the *common* C++ compilers are also C compilers.

Discussed elsewhere.

>>If you look at hobby projects, one-man toolchains and niche compilers, I
>>have never heard of any that support C++, while a working C compiler is
>>something many people have written.
>
> I don't know if its true or not, but I read once that lexx and yacc are
> quite capable of parsing C grammar and create the appropriate lex and parse
> code but the C++ syntax is apparently beyond them because there are so many
> token overloads and special cases. And that was IIRC before C++ 2011, never
> mind now.

C's grammar is not trivial to parse, due to typedefs. A typedef
effectively creates a context-specific keyword, which means the parser
has to interact with the symbol table. (Historically, this is because
typedefs were introduced relatively late in the evolution of C.)

Kaz Kylheku

unread,
Feb 6, 2023, 5:21:58 PM2/6/23
to
On 2023-02-04, Richard Damon <Ric...@Damon-Family.org> wrote:
> The key difference is that in C, the results of the conditional operator
> is always an r-value, and thus not suitable for an assignment operator.

Interestingly, unlike C++, ISO C doesn't use the term rvalue (only
lvalue). However: the term was used in the reference manual for the B
language, predecessor to C.

A footnote (I'm looking at C99, where it is note #53 on P. 46) says
"What is sometimes called ‘‘rvalue’’ is in this International Standard
described as the ‘‘value of an expression’’."

Every regular here knows this trivia; just dredging it up a bit here.

--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @Kazi...@mstdn.ca

Keith Thompson

unread,
Feb 6, 2023, 5:43:29 PM2/6/23
to
Kaz Kylheku <864-11...@kylheku.com> writes:
> On 2023-02-04, Richard Damon <Ric...@Damon-Family.org> wrote:
>> The key difference is that in C, the results of the conditional operator
>> is always an r-value, and thus not suitable for an assignment operator.
>
> Interestingly, unlike C++, ISO C doesn't use the term rvalue (only
> lvalue). However: the term was used in the reference manual for the B
> language, predecessor to C.
>
> A footnote (I'm looking at C99, where it is note #53 on P. 46) says
> "What is sometimes called ‘‘rvalue’’ is in this International Standard
> described as the ‘‘value of an expression’’."
>
> Every regular here knows this trivia; just dredging it up a bit here.

Right, and the way C (non-normatively) defines "rvalue" is a bit odd.
An lvalue is a kind of expression, but an rvalue is the result of
evaluating an expression.

Historically, lvalues and rvalues were kinds of values. An expression
could be "evaluated for its lvalue" or "evaluated for its rvalue",
depending on the context in which it appears. In particular, the left
hand side of an assignment is evaluated for its lvalue (the identify of
the object it designates), and the right hand side of an assignment is
evaluated for its rvalue. Some expressions, such as 42, have rvalues
but not lvalues; thus 42 cannot appear on the LHS of an assignment.
This probably goes back to BCPL, or perhaps even CPL.

C mostly dropped rvalues and redefined an "lvalue" as a kind of
expression.

C++ has a small menagerie of kinds of rvalues and lvalues, all of which
(I think) are kinds of expressions.

I think I prefer the historical meanings, but we're stuck with what the
standards say now. (It took the ISO C standard three tries to define
"lvalue" consistently, so I don't propose rocking that particular boat.)

Bonita Montero

unread,
Feb 7, 2023, 1:43:36 AM2/7/23
to
Am 06.02.2023 um 08:11 schrieb Paavo Helde:

> This would basically introduce a limited notion of 'reference' in the C
> language, which would make the language more complex to teach and learn.
> That's the opposite to the goals C currently has.

References are easy to learn.

Paavo Helde

unread,
Feb 7, 2023, 2:18:41 AM2/7/23
to
Yes, in the same way one push-up is easy to do.

Mut...@dastardlyhq.com

unread,
Feb 7, 2023, 4:24:59 AM2/7/23
to
On Mon, 06 Feb 2023 13:03:11 -0800
Keith Thompson <Keith.S.T...@gmail.com> wrote:
>Mut...@dastardlyhq.com writes:
>> On Sun, 5 Feb 2023 11:42:04 +0100
>> David Brown <david...@hesbynett.no> wrote:
>>>On the other hand, the vast majority of C compilers are /not/ C++
>>
>> Ok, I should have said most of the *common* C++ compilers are also C
>compilers.
>
>Discussed elsewhere.
>
>>>If you look at hobby projects, one-man toolchains and niche compilers, I
>>>have never heard of any that support C++, while a working C compiler is
>>>something many people have written.
>>
>> I don't know if its true or not, but I read once that lexx and yacc are
>> quite capable of parsing C grammar and create the appropriate lex and parse
>> code but the C++ syntax is apparently beyond them because there are so many
>> token overloads and special cases. And that was IIRC before C++ 2011, never
>> mind now.
>
>C's grammar is not trivial to parse, due to typedefs. A typedef
>effectively creates a context-specific keyword, which means the parser
>has to interact with the symbol table. (Historically, this is because
>typedefs were introduced relatively late in the evolution of C.)

Could it not cheat and simply treat a typedef as a kind of macro?

Tim Rentsch

unread,
Feb 7, 2023, 10:50:51 AM2/7/23
to
Great analogy. I tip my hat to you sir.

It's interesting to note that in his talk on cpp2, Herb Sutter
has abandoned references as a separate concept, because they
are too complicated. Instead what references are mostly used
for has been replaced by parameter passing modes.

Keith Thompson

unread,
Feb 7, 2023, 2:08:40 PM2/7/23
to
Mut...@dastardlyhq.com writes:
> On Mon, 06 Feb 2023 13:03:11 -0800
> Keith Thompson <Keith.S.T...@gmail.com> wrote:
[...]
>>C's grammar is not trivial to parse, due to typedefs. A typedef
>>effectively creates a context-specific keyword, which means the parser
>>has to interact with the symbol table. (Historically, this is because
>>typedefs were introduced relatively late in the evolution of C.)
>
> Could it not cheat and simply treat a typedef as a kind of macro?

No, typedefs have scope, and a declaration in an inner scope can hide a
typedef in an outer scope.

#include <stdio.h>
int main(void) {
typedef int Integer;
{
int Integer = 10;
printf("Integer = %d\n", Integer);
}
// int Integer = 20; // would be a syntax error

Ben Bacarisse

unread,
Feb 7, 2023, 9:52:53 PM2/7/23
to
Mut...@dastardlyhq.com writes:

> On Mon, 06 Feb 2023 13:03:11 -0800
> Keith Thompson <Keith.S.T...@gmail.com> wrote:
<cut>
>>C's grammar is not trivial to parse, due to typedefs. A typedef
>>effectively creates a context-specific keyword, which means the parser
>>has to interact with the symbol table. (Historically, this is because
>>typedefs were introduced relatively late in the evolution of C.)
>
> Could it not cheat and simply treat a typedef as a kind of macro?

Keith has mentioned the scope issue, but there are other problems with a
simple textual substitution. For example, after

typedef int *T;

the declaration const T x; gives x a const-qualified type (i.e. you
can't assign to x). A simple substitution of "T" for "int *" does not
have the same effect at all.

--
Ben.

Mut...@dastardlyhq.com

unread,
Feb 8, 2023, 4:23:58 AM2/8/23
to
On Tue, 07 Feb 2023 11:08:23 -0800
Keith Thompson <Keith.S.T...@gmail.com> wrote:
>Mut...@dastardlyhq.com writes:
>> On Mon, 06 Feb 2023 13:03:11 -0800
>> Keith Thompson <Keith.S.T...@gmail.com> wrote:
>[...]
>>>C's grammar is not trivial to parse, due to typedefs. A typedef
>>>effectively creates a context-specific keyword, which means the parser
>>>has to interact with the symbol table. (Historically, this is because
>>>typedefs were introduced relatively late in the evolution of C.)
>>
>> Could it not cheat and simply treat a typedef as a kind of macro?
>
>No, typedefs have scope, and a declaration in an inner scope can hide a
>typedef in an outer scope.
>
>#include <stdio.h>
>int main(void) {
> typedef int Integer;
> {
> int Integer = 10;
> printf("Integer = %d\n", Integer);
> }
> // int Integer = 20; // would be a syntax error
>}

Do you know I never knew you could do that with C. I thought it was C++ only.
Learnt something new today!

Blue-Maned_Hawk

unread,
Feb 8, 2023, 10:13:47 PM2/8/23
to
On 2/7/23 14:08, Keith Thompson wrote:
> Mut...@dastardlyhq.com writes:
>> On Mon, 06 Feb 2023 13:03:11 -0800
>> Keith Thompson <Keith.S.T...@gmail.com> wrote:
> [...]
>>> C's grammar is not trivial to parse, due to typedefs. A typedef
>>> effectively creates a context-specific keyword, which means the parser
>>> has to interact with the symbol table. (Historically, this is because
>>> typedefs were introduced relatively late in the evolution of C.)
>>
>> Could it not cheat and simply treat a typedef as a kind of macro?
>
> No, typedefs have scope, and a declaration in an inner scope can hide a
> typedef in an outer scope.
>

​So could it treat it as a macro that gets `#undef`ed at the end of a scope?

--
⚗︎ | /blu.mɛin.dʰak/ | shortens to "Hawk" | he/him/his/himself/Mr.
bluemanedhawk.github.io
Bitches stole my whole ass ␔🭖᷿᪳𝼗᷍⏧𒒫𐻾ࣛ↉�⃣ quoted-printable, can't
have shit in Thunderbird 😩

James Kuyper

unread,
Feb 8, 2023, 10:53:59 PM2/8/23
to
On 2/8/23 22:13, Blue-Maned_Hawk wrote:
> On 2/7/23 14:08, Keith Thompson wrote:
>> Mut...@dastardlyhq.com writes:
>>> On Mon, 06 Feb 2023 13:03:11 -0800
>>> Keith Thompson <Keith.S.T...@gmail.com> wrote:
>> [...]
>>>> C's grammar is not trivial to parse, due to typedefs. A typedef
>>>> effectively creates a context-specific keyword, which means the parser
>>>> has to interact with the symbol table. (Historically, this is because
>>>> typedefs were introduced relatively late in the evolution of C.)
>>>
>>> Could it not cheat and simply treat a typedef as a kind of macro?
>>
>> No, typedefs have scope, and a declaration in an inner scope can hide a
>> typedef in an outer scope.
>>
>
> ​So could it treat it as a macro that gets `#undef`ed at the end of a
> scope?

The same identifier can be two different typedefs in two different
nested scopes. The typedef in the inner scope hides the typedef in the
outer scope, but the typedef in the outer scope becomes visible again at
the end of the inner scope. Therefore, you'd have to have an #undef at
the end of the inner scope immediately followed by a #define.

Note: macro replacement occurs during translation phase 4. Scopes don't
even exist until translation phase 7, and can be affected by some of the
transformations that can occur during translation phase 4. That is to
say, conditional compilation, #include directives or the expansion of a
macro could create or eliminate scopes that would not have been
identified as such in the code as it existed prior to translation phase 4.

Basically, to do something similar to what you're suggesting, you need
to first perform all phase 4, 5, and 6 transformations properly, then
start translation phase 7 parsing to identify where scopes start and
end. Then you have to insert #define and #undef directives in the
appropriate places to implement typedefs, and carry out a
mini-translation phase 4 limited to recognizing those #define and #undef
directives. I don't think that this late phase 4 processing would have
any significant advantage over just biting the bullet and implementing
typedefs correctly during translation phase 7.

Keith Thompson

unread,
Feb 9, 2023, 1:13:02 AM2/9/23
to
Blue-Maned_Hawk <bluema...@gmail.com> writes:
> On 2/7/23 14:08, Keith Thompson wrote:
>> Mut...@dastardlyhq.com writes:
>>> On Mon, 06 Feb 2023 13:03:11 -0800
>>> Keith Thompson <Keith.S.T...@gmail.com> wrote:
>> [...]
>>>> C's grammar is not trivial to parse, due to typedefs. A typedef
>>>> effectively creates a context-specific keyword, which means the parser
>>>> has to interact with the symbol table. (Historically, this is because
>>>> typedefs were introduced relatively late in the evolution of C.)
>>>
>>> Could it not cheat and simply treat a typedef as a kind of macro?
>> No, typedefs have scope, and a declaration in an inner scope can
>> hide a
>> typedef in an outer scope.
>
> ​So could it treat it as a macro that gets `#undef`ed at the end of a scope?

No, that wouldn't even work for the example I posted. The "macro"
wouldn't be undefined until the end of main.

Andrey Tarasevich

unread,
Feb 10, 2023, 7:20:03 PM2/10/23
to
On 02/04/23 7:30 AM, Mut...@dastardlyhq.com wrote:
> Can anyone explain the "official" explanation from whichever standard as to
> why the first assignment only compiles in C++ but not C:

Because C is not C++, and C++ is not C.

This is one of the fundamental core differences between the languages,
which is present in all potentially applicable contexts. You just
stumbled upon one of them - the `?`: operator. There are others:

* Assignment (and compound assignment) in C++ preserves lvalue-ness of
the result

int a, b = 1;
(a = b) = 2;
// Invalid in C, valid in C++
// (and starting from C++11 the behavior is well-defined)

* Prefix increment and decrement in C++ preserves lvalue-ness of the result

int i = 0;
++(++i);
// Invalid in C, valid in C++
// (and starting from C++11 the behavior is well-defined)

C language from the very beginning never cared about preserving the
lvalue-ness of expression results. In C dereference operator (unary `*`)
and its direct derivatives (e.g. `[]` and `->`) are the only operators
that produce lvalue results. Everything else produces rvalues.

C++ completely abandoned and reworked that approach. C++ does the
opposite: it goes to great lengths in order to preserve lvalue-ness of
the result whenever possible. You can see it especially well with `?:`
operator where lvalue-preservation is obviously tricky, since the types
(and value categories) of the second and third operands might not match.
The behavior of `?:` in C++ is defined by a long set of rather
convoluted rules, which depend on how well the types and categories of
the second and third operands match. This illustrates how much effort
C++ is willing to spend on preserving something that C just discarded
nonchalantly.

On can possibly argue that the rationale behind this is introduction of
reference types in C++ (and I'd say that references is just small part
of the story), but the bottom line here is that C and C++ are languages
that adhere to diametrically opposite philosophies wrt that core issue.
These are two completely different languages. The matter can only seem
surprising to those who believe in silly myths, like "C is subset of
C++" and such.

--
Best regards,
Andrey


Armando di Matteo

unread,
Feb 21, 2023, 12:15:29 PM2/21/23
to
Andrey Tarasevich wrote:

> int a, b = 1;
> (a = b) = 2;
> // Invalid in C, valid in C++
> // (and starting from C++11 the behavior is well-defined)

What does it do, assign `b` to `a` and immediately afterwards assign 2 to `a`?

Andrey Tarasevich

unread,
Feb 21, 2023, 12:47:12 PM2/21/23
to
Yes.

--
Best regards,
Andrey


Mut...@dastardlyhq.com

unread,
Feb 22, 2023, 4:29:47 AM2/22/23
to
Wouldn't the compiler optimise this for primitive types where there will be
no side effects from the assignment and simply assign 2 to both a and b
directly?

Öö Tiib

unread,
Feb 22, 2023, 4:46:15 AM2/22/23
to
No. Compilers I tried (if set to optimise) seem to do it same way.
If `a` and `b` are further used then use constants 2 as `a`
and 1 as `b` there. If those are not used then optimise all the
posted code out totally.

Tim Rentsch

unread,
Feb 22, 2023, 9:36:50 AM2/22/23
to
Andrey Tarasevich <andreyta...@hotmail.com> writes:

> C language from the very beginning never cared about preserving the
> lvalue-ness of expression results. In C dereference operator (unary
> *`) and its direct derivatives (e.g. `[]` and `->`) are the only
> operators that produce lvalue results. Everything else produces
> rvalues.

The dot operator ('.') propagates lvalueness.

A compound literal expression gives an lvalue result.

(Sorry if this posting seems misplaced. Given the original
context, comp.lang.c++ seemed like the best choice, even
though the remarks above don't involve C++ per se.)
0 new messages