Topic: Assignment to rvalues


Author: "kanze.james@neuf.fr" <kanze.james@neuf.fr>
Date: Thu, 6 Apr 2006 09:49:05 CST
Raw View
Alf P. Steinbach wrote:
> * johnchx2@yahoo.com:
> > "Alf P. Steinbach" wrote:

> >> the requirement that /all/ assignment operators require
> >> lvalue left operand,

> > Where do you find this requirement?  The requirement in 5.17
> > doesn't apply, since 5/2 says: "Overloaded operators obey
> > the rules for syntax specified in clause 5, but the
> > requirements of operand type, lvalue, and evaluation order
> > are replaced by the rules for function call."  In other
> > words, an operator can be called with an rvalue operand if
> > the corresponding function call is legal.

> I'm not sure I follow the reasoning.

> You're saying

>    struct S {};

> has an /overloaded/ assignment operator.

> I don't see it.

That's because it is generated by the compiler.

The wording in the cited paragraph seems a bit sloppy, at least
to me.  Strictly speaking, all operator = are overloaded -- it
is overload resolution which in the end chooses between the
user-defined and that built-in operators.  I'm pretty sure,
however, that what is meant here by "overloaded operators" is
"user-defined operators".  And that, as silly as it seems, the
compiler generated default operator= is a user defined operator,
according to the definition of the standard.

> [snip]
> Anyway, there are more and more natural possibilities, such as
> (3) requiring that every assignment expression of the form

>    x = y
>    x <op>= y

> has an lvalue left operand,

Whatever its merits, this battle has been fought and lost.  How
do you implement a proxy, for example, if it were the case?

> or, (4) explicitly requiring that every application of an
> assignment operator, be it built-in, automatically generated
> or user defined, has an lvalue left operand, or (5) saying
> explicitly that a trivial generated assignment operator obeys
> the same rules as the built-in assignment operator for
> fundamental types.

The last would be a possibility.  I think that the goal is that
if you add or remove a true user defined operator (supposing
the correct semantics), the legality of the program should not
be changed.

> I favor (5) as a first step towards more rational, consistent
> and intuitive handling of this, and then also (4) so that a
> user-defined Int type can obey the same rules as  --  can
> /emulate/  --  the built-in int type...

> Also, it would be nice with a rationale for the current rules.

Try to write a proxy with any of the rules where changing from
the compiler generated form to an explicit operator= doesn't
change the legality of some program.

--
James Kanze

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: johnchx2@yahoo.com
Date: Thu, 6 Apr 2006 22:14:59 CST
Raw View
Alf P. Steinbach wrote:

> You're saying
>
>    struct S {};
>
> has an /overloaded/ assignment operator.

Well, yes, I'm saying that a type that has a compiler-generated member
function which overloads operator = has an overloaded assignment
operator.  I don't think I'm alone on this.

> I don't see it.

And yet...there it is.  :-)


> Anyway, there are more and more natural possibilities, such as (3)
> requiring that every assignment expression of the form
>
>    x = y
>    x <op>= y
>
> has an lvalue left operand, or, (4) explicitly requiring that every
> application of an assignment operator, be it built-in, automatically
> generated or user defined, has an lvalue left operand, or (5) saying
> explicitly that a trivial generated assignment operator obeys the same
> rules as the built-in assignment operator for fundamental types.
>
> I favor (5) as a first step towards more rational, consistent and
> intuitive handling of this, and then also (4) so that a user-defined Int
> type can obey the same rules as  --  can /emulate/  --  the built-in int
> type...


(3), (4), and (5) are all just specific versions of what you're calling
(2), namely breaking the principle that overloaded operators are *just*
syntactic sugar for function calls.  All involve adding extra rules
that would break working code, which simply isn't going to happen
without an overwhelming justification.


>
> Also, it would be nice with a rationale for the current rules.
>

As a rationale, I'd offer: "Overloaded operators are syntactic sugar
for function calls."

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: alfps@start.no ("Alf P. Steinbach")
Date: Fri, 7 Apr 2006 05:59:13 GMT
Raw View
* johnchx2@yahoo.com:
> Alf P. Steinbach wrote:
>=20
>> You're saying
>>
>>    struct S {};
>>
>> has an /overloaded/ assignment operator.
>=20
> Well, yes, I'm saying that a type that has a compiler-generated member
> function which overloads operator =3D has an overloaded assignment
> operator.  I don't think I'm alone on this.
>=20
>> I don't see it.
>=20
> And yet...there it is.  :-)

I still don't see that /overloaded/ operator, sorry.  I see exactly one=20
possibility for any use of "=3D" for struct S above.

AFAICS the standard does not define the term "overloaded operator".  But=20
  it does define "overloaded name".  It seems to equate "overloaded=20
operator" to user-defined operator, and, importantly, /the possibility=20
of more than one choice generated by name lookup/:

   * Note 12 "When one of these operators is overloaded (clause 13) in
     in a valid context, thus designating a user-defined operator
     function"

   * =A73.4/1 "Name lookup may associate more than one declaration with a
     name if it finds the name to be a function name; the declarations
     are said to form a set of overloaded functions (13.1)."

   * =A75.1 "Operators can be overloaded, that is, given meaning when
     applied to expressions of class type (clause 9) or enumeration
     type (7.2)".  But this one seems inconsistent, e.g., it seems to
     imply that operator& either cannot be overloaded, or by default has
     no meaning when applied to expressions of class type.  Ouch.

   ... several instances of text referring to clause 13 for definition
   of "overloaded function" ...

   Then:

   * =A713.1/1 "When two or more different declarations are specified
     for a single name in the same scope, that name is said to be
     overloaded".

Where are the two or more different declarations of operator=3D for

    struct S {};

?


>> Anyway, there are more and more natural possibilities, such as (3)
>> requiring that every assignment expression of the form
>>
>>    x =3D y
>>    x <op>=3D y
>>
>> has an lvalue left operand, or, (4) explicitly requiring that every
>> application of an assignment operator, be it built-in, automatically
>> generated or user defined, has an lvalue left operand, or (5) saying
>> explicitly that a trivial generated assignment operator obeys the same
>> rules as the built-in assignment operator for fundamental types.
>>
>> I favor (5) as a first step towards more rational, consistent and
>> intuitive handling of this, and then also (4) so that a user-defined I=
nt
>> type can obey the same rules as  --  can /emulate/  --  the built-in i=
nt
>> type...
>=20
> (3), (4), and (5) are all just specific versions of what you're calling
> (2), namely breaking the principle that overloaded operators are *just*
> syntactic sugar for function calls.  All involve adding extra rules
> that would break working code, which simply isn't going to happen
> without an overwhelming justification.

Nope, as I see it (but I may be blind... ;-)) that's not correct.

Specifically, I don't think disallowing assignment to an rvalue of class=20
type with generated assignment operator, can break any existing code.

How could it?

However, James Kanze pointed out the existing practice of using proxies,=20
  which is very relevant: and we surely do want proxies to be allocated=20
as temporaries.  I didn't think of that, and don't understand how I=20
could have managed not to.  Thanks, James; it means (3) and (4) /can/=20
break a lot of code, and so are just not on.

With the new insight from James, I now think (5) is the best alternative=20
  --  or, perhaps, ditching the distinction between rvalues and lvalues,=20
which shouldn't break any conforming code?


>> Also, it would be nice with a rationale for the current rules.
>>
>=20
> As a rationale, I'd offer: "Overloaded operators are syntactic sugar
> for function calls."

Sorry, I meant the rationale for the apparent inconsistency wrt.=20
rvalues, but thanks for trying to explain to a slow thinker like me (how=20
could it be possible to not think of proxies?).

--=20
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: kuyper@wizard.net
Date: Fri, 7 Apr 2006 08:59:28 CST
Raw View
"Alf P. Steinbach" wrote:
> * johnchx2@yahoo.com:
.
> AFAICS the standard does not define the term "overloaded operator".

Section 5p2 isn't marked as a definition the term "operator overload"
in the conventional way, by italicizing the term being defined.
However, the phrase "that is" makes it a definition:

"Operators can be overloaded, that is, given meaning when applied to
expressions of class type (clause 9) or enumeration type (7.2)."

The implicitly generated assignment operator for a class does give
meaning to the assignment operator when applied to an expression of
that classes type. The citation that lead you to question whether there
was an overloaded operator here was also from 5p2: "Operloaded
operators ovey the rules for syntax specified in clause 5, but the
requirements of operand type, lvalue, and evaluation order are replaced
by the rules for function call." I don't see any reasonable way to
interpret that statement as referring only to operators which have two
or more different overloads. If it did, you would have to know how many
overloads an operator had, in order to predict whether or not those
requirements are replaced when a particular overload is used. It that
were what was intended by 5p2, I would argue that it constitutes bad
language design.

.
> Specifically, I don't think disallowing assignment to an rvalue of class
> type with generated assignment operator, can break any existing code.
>
> How could it?

Any time you disallow something that was previously allowed, you break
any code which depended for it's correct behavior on doing that thing.
A simple rule of thumb: if a prohibition has any meaningful effect,
then it has to inconvenience someone.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "James Kanze" <kanze.james@neuf.fr>
Date: Fri, 7 Apr 2006 11:07:04 CST
Raw View
"Alf P. Steinbach" wrote:
> * johnchx2@yahoo.com:
> > Alf P. Steinbach wrote:

> >> You're saying

> >>    struct S {};

> >> has an /overloaded/ assignment operator.

> > Well, yes, I'm saying that a type that has a
> > compiler-generated member function which overloads operator
> > = has an overloaded assignment operator.  I don't think I'm
> > alone on this.

> >> I don't see it.

> > And yet...there it is.  :-)

> I still don't see that /overloaded/ operator, sorry.

Of course you do.  Any time you can use an operator with more
than one type, it is overloaded; S definitly has an overloaded
assignment operator.  So does double:-).  The use of the word
overload here is definitly an error.

I think, given the context, that what is meant is that the
specific version of the operator that will be chosen by operator
overloading is a function, rather than a built in operator, and
the standard is fairly clear that this is the case here.

> I see exactly one possibility for any use of "=" for struct S
> above.

> AFAICS the standard does not define the term "overloaded
> operator".

So we have to fall back on the usual meaning, and the way the
standard uses the verb "to overload" in other contexts.  There
are many possible operator=, and overload resolution decides
which to use.

Note that this means that an operator is either overloaded, or
not.

> But it does define "overloaded name".  It seems to equate
> "overloaded operator" to user-defined operator,

My impression is that in the context here, is that it means that
the operator is actually a function, rather than a built-in
operator.

> and, importantly, /the possibility of more than one choice
> generated by name lookup/:

Well, that's certainly the case here.

>    * Note 12 "When one of these operators is overloaded (clause 13) in
>      in a valid context, thus designating a user-defined operator
>      function"

That's an interesting comment, since it uses the words
"user-defined".  Using the usual English meanings, I think we'd
have to agree that the function here is not "user-defined" (but
the standard definitly says that it is a compiler generated
function, and not a "built-in" operator).  I don't have my copy
of the standard here, so I cannot verify that the standard
doesn't give a special meaning to "user-defined", so the best I
can do is point out that it is a note, and so non-normative.
Which is pretty weak if we agree that the normative wording
cannot be correct, and are trying to figure out what was meant.

>    *    3.4/1 "Name lookup may associate more than one declaration with a
>      name if it finds the name to be a function name; the declarations
>      are said to form a set of overloaded functions (13.1)."

>    *    5.1 "Operators can be overloaded, that is, given meaning when
>      applied to expressions of class type (clause 9) or enumeration
>      type (7.2)".  But this one seems inconsistent, e.g., it seems to
>      imply that operator& either cannot be overloaded, or by default has
>      no meaning when applied to expressions of class type.  Ouch.

>    ... several instances of text referring to clause 13 for definition
>    of "overloaded function" ...

>    Then:

>    *    13.1/1 "When two or more different declarations are specified
>      for a single name in the same scope, that name is said to be
>      overloaded".

The name in question is '='.  Are you saying that the compiler
cannot see a '=' which takes a double?

> Where are the two or more different declarations of operator= for

>     struct S {};

> ?

Where do the operator= for double and int disappear too.

--
James Kanze


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: alfps@start.no ("Alf P. Steinbach")
Date: Sat, 8 Apr 2006 01:54:07 GMT
Raw View
* kuyper@wizard.net:
> "Alf P. Steinbach" wrote:
>> * johnchx2@yahoo.com:
> .
>> AFAICS the standard does not define the term "overloaded operator".
>=20
> Section 5p2 isn't marked as a definition the term "operator overload"
> in the conventional way, by italicizing the term being defined.
> However, the phrase "that is" makes it a definition:
>=20
> "Operators can be overloaded, that is, given meaning when applied to
> expressions of class type (clause 9) or enumeration type (7.2)."

As already noted this sentence is inconsistent, e.g. for operator&.

Thus it would make a /very/ poor definition, and if it were an attempt=20
at definition, the standard needs to be corrected.

Second, "that is" is simply not a phrase that denotes a definition.=20
There are 71 instances of "that is" in the standard.  The last one is in=20
=A725.3.9, which if your interpretation is adopted defines "largest=20
permutation" as "the descendingly sorted one" (in many other cases it's=20
very unclear what would be defined, if anything).

Essentially, that interpretation takes a clarification of what is meant=20
by a statement in a local context, and erronously elevates it to a=20
context-independent global definition.

If "that is" was meant to signify a definition, in the same way as=20
italicising, then (1) that should be noted somewhere, and (2) all usages=20
that do not introduce definitions, should be removed.


> The implicitly generated assignment operator for a class does give
> meaning to the assignment operator when applied to an expression of
> that classes type. The citation that lead you to question whether there
> was an overloaded operator here was also from 5p2: "Operloaded
> operators ovey the rules for syntax specified in clause 5, but the
> requirements of operand type, lvalue, and evaluation order are replaced
> by the rules for function call." I don't see any reasonable way to
> interpret that statement as referring only to operators which have two
> or more different overloads. If it did, you would have to know how many
> overloads an operator had, in order to predict whether or not those
> requirements are replaced when a particular overload is used. It that
> were what was intended by 5p2, I would argue that it constitutes bad
> language design.

The simplest way to resolve this inconsistency in the standard (and it=20
is an inconsistency), is probably to assume that the word "overloaded"=20
is incorrectly used in =A75/2.

That would explain the attempt at clarification, the "that is", which,=20
being inconsistent for operator& and more, just muddles the waters.

I think what's meant in =A75/2 is user-defined and automatically generate=
d=20
operators.


>> Specifically, I don't think disallowing assignment to an rvalue of cla=
ss
>> type with generated assignment operator, can break any existing code.
>>
>> How could it?
>=20
> Any time you disallow something that was previously allowed, you break
> any code which depended for it's correct behavior on doing that thing.
> A simple rule of thumb: if a prohibition has any meaningful effect,
> then it has to inconvenience someone.

Nope.  It has to inconvenience or /convenience/ someone, like, making it=20
easier to detect meaningless (and therefore probably incorrect) code.=20
Although in this case I think you're right about the potential, very=20
very potential, existence of constructs that could be broken, e.g.

   (S() =3D someStructValue).display()

where display() has the side-effect of changing the object it's applied=20
to; S, very inconveniently, does not have an available copy constructor;=20
and the point being to display someStructValue without changing it, and=20
without introducing a new name.

Summing up, the standard seems to be clearly inconsistent, but it's not=20
trivial to fix it.

--=20
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: johnchx2@yahoo.com
Date: Fri, 7 Apr 2006 20:52:23 CST
Raw View
"Alf P. Steinbach" wrote:

> I still don't see that /overloaded/ operator, sorry.  I see exactly one
> possibility for any use of "=" for struct S above.

It turns out not to matter.  13.5/7 says: "Some predefined operators,
such as +=, require an operand to be an lvalue when applied to basic
types; this is not required by operator functions."  I should think
that that is sufficiently clear.

> Specifically, I don't think disallowing assignment to an rvalue of class
> type with generated assignment operator, can break any existing code.
>
> How could it?

Well, in the simple sense that it makes illegal doing something that is
currently legal.  There are really two possibilities in practice:

(A) Nobody has ever assigned to an rvalue of class type with a trivial
assignment operator function.  If that were the case, what would we be
gaining?  (See below on the question of consistency.)

(B) Somebody somewhere wrote code assigning to an rvalue of class type
with a trivial assignment operator function.  That code will no longer
compile.

My guess would be that there is *some* offending code out there in the
world somewhere, but that you would argue that there *shouldn't* be --
i.e. that there's no reason to write the code that way, and that it
would be easy to fix.

And that would be a perfectly acceptable argument, if accompanied by a
compelling reason that the rule needs to be changed.  Breaking user
code (or, as the implementers like to call it, *customer* code) to
"purify" the language standard is probably not a winning case.


> With the new insight from James, I now think (5) is the best alternative

Hmmm.  I suppose you probably won't agree, but doesn't this actually
*reduce* consistency?

Here's my thinking: in the current standard, there are two sets of
rules: one for built-in types and one for class-types.  The rule for
class-types is simple (...syntactic sugar for function calls...), and
it applies to *all* class types, all the time.

Under proposal 5, we now have two sets of rules for class-types,
depending on the triviality of its assignment operator function.  If
the assignment operator is trivial, the rules are "built-in-like," and
if the assignment operator is non-trivial (even if it *is* compiler
generated), the rule is "syntactic sugar for function call".

Now, suppose a class has a compiler generated assignment operator, and
one or more private member objects.  The triviality of the assignment
operator depends upon the triviality of the assignment operators of the
member objects.  So to figure out which set of rules applies, the user
of the class needs to know about the implementation of the assignment
operators of the class's private member objects.

A change to the implementation of one of the private member objects, or
the addition or deletion of a private member object, could affect the
triviality of the enclosing object's assignment operator.  This makes
the legal uses of the enclosing class awfully dependent on the
implementation details of its private innards.

(The same, more or less, applies to base classes as well.)

For classes with trivial assignment operators, a further question
arises: exactly how "built-in-like" are the rules?  In particular, does
trivial assignment impose a sequence point (like a function call) or
not (like assignment to a built-in)?

Overall, I have trouble seeing this as an improvement.


>   --  or, perhaps, ditching the distinction between rvalues and lvalues,
> which shouldn't break any conforming code?

This, I think, is a more interesting avenue for exploration.  I'm not
at all sure the road goes anywhere, but the obstacles would be
interesting ones.

Let me frame the question this way: "What would be the drawbacks of
adding a standard rvalue-to-lvalue conversion?"

The standard seems to be written to give compilers the leeway to return
built-in types from functions in a register (rvalues do not necessarily
denote "objects", i.e. regions of storage).  But surely that's possible
under the as-if rule, isn't it?  In addition, the standard give the
compiler leeway to make extra copies of rvalues in some situations
(e.g. binding a reference to a temporary).  But writing a value from a
register to a memory address isn't "copying" in any meaningful sense.

The only problem I see off the top of my head is that this would break
the "safety feature" which forbids initializing a non-const reference
with an rvalue.    (Personally, I'd favor a more restricted "safety
feature," along the lines of forbidding initialization of a non-const
reference with the result of an implicit conversion from or to a
built-in type.)

I suspect that there is deep compiler magic lurking somewhere which
makes the notion of a standard rvalue-to-lvalue conversion a bad idea,
but I'd be curious to know exactly what it is.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: kuyper@wizard.net
Date: Sat, 8 Apr 2006 12:00:41 CST
Raw View
"Alf P. Steinbach" wrote:
> * kuyper@wizard.net:
> > "Alf P. Steinbach" wrote:
> >> * johnchx2@yahoo.com:
> > .
> >> AFAICS the standard does not define the term "overloaded operator".
> >
> > Section 5p2 isn't marked as a definition the term "operator overload"
> > in the conventional way, by italicizing the term being defined.
> > However, the phrase "that is" makes it a definition:
> >
> > "Operators can be overloaded, that is, given meaning when applied to
> > expressions of class type (clause 9) or enumeration type (7.2)."
>
> As already noted this sentence is inconsistent, e.g. for operator&.
>
> Thus it would make a /very/ poor definition, and if it were an attempt
> at definition, the standard needs to be corrected.

I agree that it's not an ideal definition, I wouldn't object to a
clean-up to cover cases like operator&.

> Second, "that is" is simply not a phrase that denotes a definition.
> There are 71 instances of "that is" in the standard.  The last one is in
>    25.3.9, which if your interpretation is adopted defines "largest
> permutation" as "the descendingly sorted one" (in many other cases it's
> very unclear what would be defined, if anything).

Saying "A, that is, B." identifies B as a further explanation of A.
Whether this further explanation defines A, or merely describes it,
depends on the context. In 5.2, A is introducing the general concept of
operator overloading, so the explanation in B has to be general too. It
could simply be a general description that applies to all operator
overloads, but since "operator overload" has no other definition, and
this clause says something about "operator overloads" that can't be
derived from considering the two terms seperately, I think that it does
constitute a definition, albeit an imperfect one. Operators aren't, in
C terms, functions (though in some cases that's precisely how they're
translated by the compiler). Therefore, if it weren't for the fact that
this particular clause gives that term a meaning, it wouldn't be
meaningful to talk about an operator overload.

In 25.3.9, A refers to the "largest permutation" in the particular
context of a call to this function; B describes a characterstic that
this largest permutation necessarily has in that particular context -
it does not describe a characterstic of the the "largest permutation"
that applies in all contexts, and in particular, it doesn't describe
the characteristics that define it as being the largest permutation.

> > The implicitly generated assignment operator for a class does give
> > meaning to the assignment operator when applied to an expression of
> > that classes type. The citation that lead you to question whether there
> > was an overloaded operator here was also from 5p2: "Operloaded
> > operators ovey the rules for syntax specified in clause 5, but the
> > requirements of operand type, lvalue, and evaluation order are replaced
> > by the rules for function call." I don't see any reasonable way to
> > interpret that statement as referring only to operators which have two
> > or more different overloads. If it did, you would have to know how many
> > overloads an operator had, in order to predict whether or not those
> > requirements are replaced when a particular overload is used. It that
> > were what was intended by 5p2, I would argue that it constitutes bad
> > language design.
>
> The simplest way to resolve this inconsistency in the standard (and it
> is an inconsistency), is probably to assume that the word "overloaded"
> is incorrectly used in    5/2.

Every overloadable operator also currently has meanings for one or more
(usually "more") of the standard types. Therefore, anything that gives
the operator a meaning for a user-defined type IS an overload; that
includes the implicitly generated operator overloads.

> I think what's meant in    5/2 is user-defined and automatically generated
> operators.

I agree. And since any user-defined or automatically generated operator
is an overload, no re-wording is needed to capture that meaning.

> >> Specifically, I don't think disallowing assignment to an rvalue of class
> >> type with generated assignment operator, can break any existing code.
> >>
> >> How could it?
> >
> > Any time you disallow something that was previously allowed, you break
> > any code which depended for it's correct behavior on doing that thing.
> > A simple rule of thumb: if a prohibition has any meaningful effect,
> > then it has to inconvenience someone.
>
> Nope.  It has to inconvenience or /convenience/ someone, like, making it
> easier to detect meaningless (and therefore probably incorrect) code.

If a prohibiting something doesn't inconvenience anyone, then it it
doesn't need to prohibited. It's perfectly feasible for a compiler to
detect and warn about meaningless code even if it's not prohibited. Of
course, since the code you're talking about prohibiting is not
meaningless, that's just a side issue.

Assigning to an rvalue of built-in type is meaningless. However, since
the assignment operator for a class type is a function (even if
implicitly generated), it can directly or indirectly do things such as
write to a file or a device, or even just simply changing the value of
a variable, that involve something other than the rvalue itself. As a
result, it's entirely possible, indeed, trivial, for it to be the case
that the correct behavior of the program relies upon that code.
Granted, implicitly generated assignment operators do none of those
things directly, but if a class member itself has a class type, then
the implicitly generated assignment operator will invoke the
constructor for the member's class, which might do any of those things.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: alfps@start.no ("Alf P. Steinbach")
Date: Wed, 5 Apr 2006 19:02:57 GMT
Raw View
The standard is a bit unclear.

Given

   struct S { int x; };
   S f() { return S(); }

Then

   f().operator=( something );              // A

seems to be allowed, because you can call a member function on an rvalue.

   f() = something;                         // B

is perhaps allowed (just yesterday I was 100% sure it wasn't, because of
the requirement that /all/ assignment operators require lvalue left
operand, but this case is very very fishy, AFAICS with no clear language
in the standard).

   f().x = something;                       // C

is clearly disallowed, can't assign to rvalue, no no.

If B is allowed but C is not, saying that in case C hey you can't assign
to an rvalue, but in B it's OK, that smells like inconsistency to me:
that you can assign to the whole object, which assignment is defined and
executed in terms of parts, but not directly to a part of the object.

I'd like either all of A, B and C to be disallowed, or to be allowed.


--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: NULL@NULL.NULL ("Tom s")
Date: Wed, 5 Apr 2006 20:58:21 GMT
Raw View

This point has been mentioned a few times on comp.lang.c++

I myself initially came across it one day when I was pondering over why o=
r=20
why not you would declare a return value as const, e.g.:

    const int Funct() { int k =3D 5; return k; }

At first, I had reasoned that it makes no difference as to whether or not=
=20
you put the "const" there, because return values are not l-values. Thus, =
I=20
figured that return values were all implicitly const, something like:

    const int main()
    {

    }

being the same as:

    int main()
    {

    }

Then, someone brought my attention to a nice little technicality: Just=20
because something is not an l-value, doesn't mean it's const. Forgive me =
for=20
not giving an exact quotation, but my understanding of an "l-value" is=20
something which can appear on the left hand side of an assignment stateme=
nt,=20
e.g.:

    int i =3D 4;

    i =3D 5;
    i +=3D 7;
    i *=3D 2;
    i |=3D 3;

If you have an object which has member objects and member functions, then=
=20
they can be accessed and invoked on an l-value. The following is pefectly=
=20
legal.

#include <iostream>
#include <string>

std::string SomeFunc()
{
    return "monkey";
}

int main()
{
    std::cout << SomeFunc().c_str();
}


The assignment operator can be accessed just like any other member functi=
on.=20
So again, the following is perfectly legal:


#include <iostream>
#include <string>

std::string SomeFunc()
{
    return "monkey";
}

int main()
{
    std::cout << SomeFunc().operator=3D("ape");
}



The following, strictly speaking (as regards my own interpretation of the=
=20
Standard), should be illegal:

#include <iostream>
#include <string>

std::string SomeFunc()
{
    return "monkey";
}

int main()
{
    std::cout << ( SomeFunc() =3D "ape" );
}


It should be illegal because an assignment requires an "l-value". The obj=
ect=20
returned from a function by value is not an "l-value". I tried to compile=
 it=20
and, to my surprise, it compiled without error. If memory serves me right=
, I=20
tried to compile the same code about a year ago on a previous version of =
the=20
same compiler, and it gave a compile error.

I believe that the Standard should explicitly state whether the following=
=20
code is legal:

#include <iostream>
#include <string>

std::string SomeFunc()
{
    return "monkey";
}

int main()
{
    std::cout << ( SomeFunc() =3D "ape" );
}


Anyway, the moral of the story is that I found out what happens when you=20
declare a return value as const. If the return type is a primitive type,=20
then it makes absolutely no difference. If, however, the type is user=20
defined, then it certainly does make a difference:

#include <iostream>
#include <string>

std::string const SomeFunc()
{
    return "monkey";
}

int main()
{
    std::cout << ( SomeFunc().operator=3D("monkey_poo") );
    //won't compile
}


-Tom=E1s

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: johnchx2@yahoo.com
Date: Wed, 5 Apr 2006 16:02:13 CST
Raw View
"Alf P. Steinbach" wrote:

> the requirement that /all/ assignment operators require lvalue left
> operand,

Where do you find this requirement?  The requirement in 5.17 doesn't
apply, since 5/2 says: "Overloaded operators obey the rules for syntax
specified in clause 5, but the requirements of operand type, lvalue,
and evaluation order are replaced by the rules for function call."  In
other words, an operator can be called with an rvalue operand if the
corresponding function call is legal.

>
> I'd like either all of A, B and C to be disallowed, or to be allowed.
>

A and B are legal.  (I'd say "clearly" legal, because of the language
in 5/2, but I suppose others could disagree.)  The status of C is
strictly speaking, unclear, since 5.2.5/4 doesn't actually say that
E1.E2 is an rvalue if E1 is an rvalue...but that was almost surely the
intent.  See:

  http://www2.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#421

g++ accepts example C, but a bug report has been filed on this
(#15218), so let's assume that everyone agrees that C should be
illegal.

To get the result you want, we'd either have to make assignment to an
rvalue of built-in type legal (essentially abolishing the distinction
between lvalues and rvalues) or break the principle that operator
overloading is nothing more than syntactic sugar for an otherwise
undistinguished function call (and break working code in the process).

I think I understand the kind of consistency you're trying to create,
but IMHO it  is a misguided sort of consistency.  An overloaded
operator really *isn't* the same thing as the built-in operator with
the same spelling.  (Think, e.g., about sequence points.)  Since we
can't make them *really* alike, it's better just to teach the
differences and learn to live with them.  IMHO.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Alf P. Steinbach" <alfps@start.no>
Date: Wed, 5 Apr 2006 17:37:49 CST
Raw View
* johnchx2@yahoo.com:
> "Alf P. Steinbach" wrote:
>
>> the requirement that /all/ assignment operators require lvalue left
>> operand,
>
> Where do you find this requirement?  The requirement in 5.17 doesn't
> apply, since 5/2 says: "Overloaded operators obey the rules for syntax
> specified in clause 5, but the requirements of operand type, lvalue,
> and evaluation order are replaced by the rules for function call."  In
> other words, an operator can be called with an rvalue operand if the
> corresponding function call is legal.

I'm not sure I follow the reasoning.

You're saying

   struct S {};

has an /overloaded/ assignment operator.

I don't see it.


[snip]
> To get the result you want, we'd either have to make assignment to an
> rvalue of built-in type legal (essentially abolishing the distinction
> between lvalues and rvalues) or break the principle that operator
> overloading is nothing more than syntactic sugar for an otherwise
> undistinguished function call (and break working code in the process).

I agree that the first possibility is a possibility, let's call it (1).
  I'm not sure about the second, let's call it (2), because I'm not sure
that an empty POD struct has an /overloaded/ assignment operator.

Anyway, there are more and more natural possibilities, such as (3)
requiring that every assignment expression of the form

   x = y
   x <op>= y

has an lvalue left operand, or, (4) explicitly requiring that every
application of an assignment operator, be it built-in, automatically
generated or user defined, has an lvalue left operand, or (5) saying
explicitly that a trivial generated assignment operator obeys the same
rules as the built-in assignment operator for fundamental types.

I favor (5) as a first step towards more rational, consistent and
intuitive handling of this, and then also (4) so that a user-defined Int
type can obey the same rules as  --  can /emulate/  --  the built-in int
type...

Also, it would be nice with a rationale for the current rules.


--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Wed, 5 Apr 2006 22:36:46 GMT
Raw View
Alf P. Steinbach wrote:
> If B is allowed but C is not, saying that in case C hey you can't assign
> to an rvalue, but in B it's OK, that smells like inconsistency to me:

there is indeed an incosistency, that allows non-const methods (including
operator=) to be called on temporary objects of user-defined type. The
"backdoor" is in the wording of clause 13.3.1 that says :

--- citation begin
For non-static member functions, the type of the implicit object parameter is
"reference to cv X" where X is the class of which the function is a member and
cv is the cv-qualification on the member function declaration. [...]
* even if the implicit object parameter is not const-qualified, an rvalue
temporary can be bound to the parameter as long as in all other respects the
temporary can be converted to the type of the implicit object parameter.
--- citation end

Thanks to rvalue-references (N1855 and preceding revisions) we currently have
a chance to fix it and are working on resolution. You may find (unfortunatelly
  flawed - there will be revision) attempt in paper N1821. Sadly, as we cannot
change meaning of existing programs, resolution will add to the complexity of
the language (although not by much).


B.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]