Topic: Side effects in operators


Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/25
Raw View
jpotter@falcon.lhup.edu (John E. Potter) writes:

|>  : |>  I guess anything is possible, but I can see why prospective library
|>  : |>  writers would want maximal specification.
|>
|>
|>  : And prospective library users would want the minimum.  So who do we
|>  : favorize.
|>
|>  Hummm.  I guess you caught be being procedural.  I was thinking of
|>  classes as servers and functions as clients.  The operator= in a class
|>  is a service provided by the class to the user.  I was thinking of
|>  the algorithms library as a client of the operator= in the class of
|>  objects in the container.  A copy assignment operator which returns
|>   *this gives the user the most flexability.

And the *classes* in the standard library should provide this.

My point is that the standard library (both classes and functions) is a
service to the user, and should make the least possible requirements on
him.  Sort of the Internet principle: be conservative in what you send,
and liberal in what you accept.  Thus, the library classes should
implement a maximum flexibility, but the library functions should NOT
require that user classes be this flexible.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/13
Raw View
jpotter@falcon.lhup.edu (John E. Potter) writes:

|>  James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
|>  : jpotter@falcon.lhup.edu (John E. Potter) writes:
|>  :
|>  : |>  Joe Keane (jgk@jgk.org) wrote:
|>  : |>  : I'm rather dubious about the convention of having operators return one
|>  : |>  : of their arguments.  It's important to note that the result is
|>  : |>  : entirely
|>  : |>  : syntactic sugar; it's semantically useless to return the first
|>  : |>  : argument.
|>  : |>  : If you're going to return a value, anything would be more useful.
|>
|>  : |>  : If things were up to me, i would always have assignments return
|>  : |>  : `void'.
|>  : |>  : That prevents such overly nifty code, which i think is a good thing.
|>  : |>  : But that's my style, and of course other people may disagree.
|>
|>  : |>  : Now to my point:  Why does STL require this convention?  The STL code
|>  : |>  : can easily be written to never use the result of an assignment, right?
|>
|>  : |>  Certainly.  But, should the international standard require all
|>  : |>  implementers
|>  : |>  to code the algorithms your way?  Or, is it reasonable to force you to
|>  : |>  return *this without forcing you to use the result?  IMHO, there is a
|>  : |>  compromise between your extreme and the nifty-code extreme which is
|>  : |>  clearer to almost everyone.
|>
|>  : The international standard doesn't even require implementors to code the
|>  : algorithms; all that it requires is that when I use them as specified,
|>  : they work as specified.  In this case, Joe Keane has proposed a coding
|>  : rule which should, IMHO, be legal (although it is not the rule I use
|>  : myself).  The standard should favor the coder: if Joe thinks that this
|>  : rule makes coding easier for him, the standard should not make it
|>  : illegal, unless it imposes a significant cost on others who do not use
|>  : the rule.  In this case, I see no significant cost for a normal
|>  : compiler.
|>
|>  : FWIW: the additional effort for the implementors is minimal anyway.
|>
|>  OK, I see your point.  My thoughts run along the lines of <cstdlib>
|>  includes a function qsort.  It uses a callback function and to use
|>  it, I must have a function which matches the prototype specified.  It
|>  best also have the required semantics.
|>
|>  With STL, member functions used in the templates take the place of the
|>  callback functions.  It seems reasonable for the standard to specify
|>  the prototype.  If so, the only possible prototype is that of the
|>  builtins since the algorithms must work for them.
|>
|>  I think that there is no problem with a prototype for comparison
|>  operators.  I think that you are saying that a signature is enough for
|>  the copy assignment operator.  Or are you saying that any of the five
|>  valid signatures should be allowed?  And with any return type?

I am saying that the standard should specify the required operation, not
how it is implemented.  In particular, in the case of assignment, the
required operation is that the expression "v = x" be valid for all v
which evaluate to an lvalue of type T, and all x which evaluate to a
type T.  Such a definition says nothing about the return type of
operator=.  In fact, IMHO, the rule should be even looser, with the
exact forms of "v" (and maybe "x") specified, so that helper classes may
play a role.  (In practice, I'm not how sure how useful this relaxation
of the rules would be.)

|>  I guess anything is possible, but I can see why prospective library
|>  writers would want maximal specification.

And prospective library users would want the minimum.  So who do we
favorize.

|>  And with all of these nice containers and smart pointers, we are all
|>  allowing the compiler to generate our assignment operators and they
|>  will automagically match the requirement :-)

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: David Vandevoorde <daveed@vandevoorde.com>
Date: 1997/02/16
Raw View
James Kanze wrote:
[...]
> I am saying that the standard should specify the required operation,
> not how it is implemented.  In particular, in the case of assignment, the
> required operation is that the expression "v = x" be valid for all v
> which evaluate to an lvalue of type T, and all x which evaluate to a
> type T.  Such a definition says nothing about the return type of
> operator=.  In fact, IMHO, the rule should be even looser, with the
> exact forms of "v" (and maybe "x") specified, so that helper classes
> may play a role.  (In practice, I'm not how sure how useful this
> relaxation of the rules would be.)

Such a relaxation was actually introduced for the valarray types
(Stockholm meeting). The purpose in that case was to make efficient
C++ (as opposed to ``magic'') implementations feasible. Making such
abstract specifications precise can be tricky.

 Daveed
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/16
Raw View
John E. Potter wrote:
[...]
> OK, I see your point.  My thoughts run along the lines of <cstdlib>
> includes a function qsort.  It uses a callback function and to use
> it, I must have a function which matches the prototype specified.  It
> best also have the required semantics.

Function ptr have to match exactly; there is no possible
conversion betwen them (except tricks that work on a
particular machine).

> With STL, member functions used in the templates take the place of the
> callback functions.  It seems reasonable for the standard to specify
> the prototype.  If so, the only possible prototype is that of the
> builtins since the algorithms must work for them.

No, and the standard does not specify the prototype at all.
It specifies a correct use, giving liberties to both lib
implementors and users (which is good).

For example, when you say *p++ must be valid, you don't
tell the signature of operator++ (int); it could return
a pointer, an iterator, ect...

So I think that the requirement 'a = b' works should be
sufficient (with all the necessary pre/postconditions).

> I think that there is no problem with a prototype for comparison
> operators.  I think that you are saying that a signature is enough for
> the copy assignment operator.  Or are you saying that any of the five
> valid signatures should be allowed?  And with any return type?

I think they should all be allowed; return value, if there is
one, shouldn't be used.

> I guess anything is possible, but I can see why prospective library
> writers would want maximal specification.

I don't see why (the current rules aren't very strict anyway).

> And with all of these nice containers and smart pointers, we are all
> allowing the compiler to generate our assignment operators and they
> will automagically match the requirement :-)

:-)

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/18
Raw View
James Kanze (james-albert.kanze@vx.cit.alcatel.fr) wrote:
: jpotter@falcon.lhup.edu (John E. Potter) writes:

: |>  I think that you are saying that a signature is enough for
: |>  the copy assignment operator.  Or are you saying that any of the five
: |>  valid signatures should be allowed?  And with any return type?


: I am saying that the standard should specify the required operation, not
: how it is implemented.  In particular, in the case of assignment, the
: required operation is that the expression "v = x" be valid for all v
: which evaluate to an lvalue of type T, and all x which evaluate to a
: type T.  Such a definition says nothing about the return type of
: operator=.


Well said.  Just for curiosity, I did a grep on the sgi stl files.  There
was one line repeated in two files which used the form

   A = B = C;

Should not be much of a hardship to fix that if the rules were relaxed.


: |>  I guess anything is possible, but I can see why prospective library
: |>  writers would want maximal specification.


: And prospective library users would want the minimum.  So who do we
: favorize.


Hummm.  I guess you caught be being procedural.  I was thinking of
classes as servers and functions as clients.  The operator= in a class
is a service provided by the class to the user.  I was thinking of
the algorithms library as a client of the operator= in the class of
objects in the container.  A copy assignment operator which returns
 *this gives the user the most flexability.

I still hold same view on what I think operator= should look like.  But
just because the draft requires what I like does not justify it.  The
algorithms library may be users of the class; however, the class
author is a user of the library and it should not impose restrictions
needlessly.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/11
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
: jpotter@falcon.lhup.edu (John E. Potter) writes:
:
: |>  Joe Keane (jgk@jgk.org) wrote:
: |>  : I'm rather dubious about the convention of having operators return one
: |>  : of their arguments.  It's important to note that the result is
: |>  : entirely
: |>  : syntactic sugar; it's semantically useless to return the first
: |>  : argument.
: |>  : If you're going to return a value, anything would be more useful.

: |>  : If things were up to me, i would always have assignments return
: |>  : `void'.
: |>  : That prevents such overly nifty code, which i think is a good thing.
: |>  : But that's my style, and of course other people may disagree.

: |>  : Now to my point:  Why does STL require this convention?  The STL code
: |>  : can easily be written to never use the result of an assignment, right?

: |>  Certainly.  But, should the international standard require all
: |>  implementers
: |>  to code the algorithms your way?  Or, is it reasonable to force you to
: |>  return *this without forcing you to use the result?  IMHO, there is a
: |>  compromise between your extreme and the nifty-code extreme which is
: |>  clearer to almost everyone.

: The international standard doesn't even require implementors to code the
: algorithms; all that it requires is that when I use them as specified,
: they work as specified.  In this case, Joe Keane has proposed a coding
: rule which should, IMHO, be legal (although it is not the rule I use
: myself).  The standard should favor the coder: if Joe thinks that this
: rule makes coding easier for him, the standard should not make it
: illegal, unless it imposes a significant cost on others who do not use
: the rule.  In this case, I see no significant cost for a normal
: compiler.

: FWIW: the additional effort for the implementors is minimal anyway.

OK, I see your point.  My thoughts run along the lines of <cstdlib>
includes a function qsort.  It uses a callback function and to use
it, I must have a function which matches the prototype specified.  It
best also have the required semantics.

With STL, member functions used in the templates take the place of the
callback functions.  It seems reasonable for the standard to specify
the prototype.  If so, the only possible prototype is that of the
builtins since the algorithms must work for them.

I think that there is no problem with a prototype for comparison
operators.  I think that you are saying that a signature is enough for
the copy assignment operator.  Or are you saying that any of the five
valid signatures should be allowed?  And with any return type?

I guess anything is possible, but I can see why prospective library
writers would want maximal specification.

And with all of these nice containers and smart pointers, we are all
allowing the compiler to generate our assignment operators and they
will automagically match the requirement :-)

Enjoy,
John


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/11
Raw View
James Kanze (james-albert.kanze@vx.cit.alcatel.fr) wrote:
: jpotter@falcon.lhup.edu (John E. Potter) writes:
:
: |> Joe Keane (jgk@jgk.org) wrote:
: |> : I'm rather dubious about the convention of having operators return one
: |> : of their arguments.  It's important to note that the result is entirely
: |> : syntactic sugar; it's semantically useless to return the first
: |> : argument.
: |> : If you're going to return a value, anything would be more useful.

: |> : If things were up to me, i would always have assignments return `void'.
: |> : That prevents such overly nifty code, which i think is a good thing.
: |> : But that's my style, and of course other people may disagree.

: |> : Now to my point:  Why does STL require this convention?  The STL code
: |> : can easily be written to never use the result of an assignment, right?

: |>  Certainly.  But, should the international standard require all
: |>  implementers
: |>  to code the algorithms your way?  Or, is it reasonable to force you to
: |>  return *this without forcing you to use the result?  IMHO, there is a
: |>  compromise between your extreme and the nifty-code extreme which is
: |>  clearer to almost everyone.

: The international standard doesn't even require implementors to code the
: algorithms; all that it requires is that when I use them as specified,
: they work as specified.  In this case, Joe Keane has proposed a coding
: rule which should, IMHO, be legal (although it is not the rule I use
: myself).  The standard should favor the coder: if Joe thinks that this
: rule makes coding easier for him, the standard should not make it
: illegal, unless it imposes a significant cost on others who do not use
: the rule.  In this case, I see no significant cost for a normal
: compiler.

: FWIW: the additional effort for the implementors is minimal anyway.

OK, I see your point.  My thoughts run along the lines of <cstdlib>
includes a function qsort.  It uses a callback function and to use
it, I must have a function which matches the prototype specified.  It
best also have the required semantics.

With STL, member functions used in the templates take the place of the
callback functions.  It seems reasonable for the standard to specify
the prototype.  If so, the only possible prototype is that of the
builtins since the algorithms must work for them.

I think that there is no problem with a prototype for comparison
operators.  I think that you are saying that a signature is enough for
the copy assignment operator.  Or are you saying that any of the five
valid signatures should be allowed?  And with any return type?

I guess anything is possible, but I can see why prospective library
writers would want maximal specification.

And with all of these nice containers and smart pointers, we are all
allowing the compiler to generate our assignment operators and they
will automagically match the requirement ;-)

Enjoy,
John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: hpa@transmeta.com (H. Peter Anvin)
Date: 1997/02/11
Raw View
Followup to:  <32F64D51.31E5@pratique.fr>
By author:    Valentin Bonnard <bonnardv@pratique.fr>
In newsgroup: comp.std.c++
>
> So the string class must be ill-designed...
>
> Possible rules are:
>
> + is commutative  a + b == b + a       OK except for basic_string
> * is commutative  a * b == b * a       not for a matrix
> + is associative  (a+b)+c == a+(b+c)   not for floating points types
> * is multilinear  (a+d)*b == a*b + d*b not for floating points types
>
> A rule might be to follow the mathematical properties with
> approximations.
>

Hmmm... this brings up something I have been thinking for a
while... the whole issue of optimizing classes (optimization hints.)
For example:

attribute(mathematical) complex sin(complex);

What I mean, here, with a "mathematical" function is one which does
nothing but provide a mapping from a set of inputs to a set of
outputs, idempotently, with no side effects.  The complex sine is one
such example: sin(complex(1,0)) is always going to return the same
value no matter what.  This would permit a compiler to perform common
subexpression elimination as well as compile-time (or
initialization-time) evaluation of constants.

Other attributes could include commutative, associative,
distributive, to enable the compiler to apply arithmetric
optimizations.

 -hpa
--
This space intentionally has nothing but text explaining why this
space has nothing but text explaining that this space would otherwise
have been left blank, and would otherwise have been left blank.



[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/03
Raw View
Joe Keane (jgk@jgk.org) wrote:
: I'm rather dubious about the convention of having operators return one
: of their arguments.  It's important to note that the result is entirely
: syntactic sugar; it's semantically useless to return the first argument.
: If you're going to return a value, anything would be more useful.

[ comments about removing COMPUTE from COBOL snipped ]

: If things were up to me, i would always have assignments return `void'.
: That prevents such overly nifty code, which i think is a good thing.
: But that's my style, and of course other people may disagree.

: Now to my point:  Why does STL require this convention?  The STL code
: can easily be written to never use the result of an assignment, right?

Certainly.  But, should the international standard require all implementers
to code the algorithms your way?  Or, is it reasonable to force you to
return *this without forcing you to use the result?  IMHO, there is a
compromise between your extreme and the nifty-code extreme which is
clearer to almost everyone.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/03
Raw View
James Kuyper wrote:
> The point of operator overloading is to allow you to write code using
> user defined types that has the same syntax as analogous code using the
> basic types. If your overloaded operator doesn't work in an analogous
> way, you shouldn't be using a overloaded operator. Instead, you should
> define a named member function. For example, if (A+B) != (B+A), then
> either '+' or '!=' has probably been overloaded inappropriately.
> Many operators, when applied to an object of one of the basic types,
> return that object, which can be passed on to further operators. In
> order to write analogous code using an overloaded operator, it must do
> the same thing.

So the string class must be ill-designed...

Possible rules are:

+ is commutative  a + b == b + a       OK except for basic_string
* is commutative  a * b == b * a       not for a matrix
+ is associative  (a+b)+c == a+(b+c)   not for floating points types
* is multilinear  (a+d)*b == a*b + d*b not for floating points types

A rule might be to follow the mathematical properties with
approximations.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/04
Raw View
jpotter@falcon.lhup.edu (John E. Potter) writes:

|>  Joe Keane (jgk@jgk.org) wrote:
|>  : I'm rather dubious about the convention of having operators return one
|>  : of their arguments.  It's important to note that the result is entirely
|>  : syntactic sugar; it's semantically useless to return the first argument.
|>  : If you're going to return a value, anything would be more useful.
|>
|>  [ comments about removing COMPUTE from COBOL snipped ]
|>
|>  : If things were up to me, i would always have assignments return `void'.
|>  : That prevents such overly nifty code, which i think is a good thing.
|>  : But that's my style, and of course other people may disagree.
|>
|>  : Now to my point:  Why does STL require this convention?  The STL code
|>  : can easily be written to never use the result of an assignment, right?
|>
|>  Certainly.  But, should the international standard require all implementers
|>  to code the algorithms your way?  Or, is it reasonable to force you to
|>  return *this without forcing you to use the result?  IMHO, there is a
|>  compromise between your extreme and the nifty-code extreme which is
|>  clearer to almost everyone.

The international standard doesn't even require implementors to code the
algorithms; all that it requires is that when I use them as specified,
they work as specified.  In this case, Joe Keane has proposed a coding
rule which should, IMHO, be legal (although it is not the rule I use
myself).  The standard should favor the coder: if Joe thinks that this
rule makes coding easier for him, the standard should not make it
illegal, unless it imposes a significant cost on others who do not use
the rule.  In this case, I see no significant cost for a normal
compiler.

FWIW: the additional effort for the implementors is minimal anyway.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Marcelo Cantos <marcelo@mds.rmit.edu.au>
Date: 1997/02/04
Raw View
John E. Potter wrote:
>
> Joe Keane (jgk@jgk.org) wrote:
> : I'm rather dubious about the convention of having operators return one
> : of their arguments.  It's important to note that the result is entirely
> : syntactic sugar; it's semantically useless to return the first argument.
> : If you're going to return a value, anything would be more useful.
>
> [ comments about removing COMPUTE from COBOL snipped ]
>
> : If things were up to me, i would always have assignments return `void'.
> : That prevents such overly nifty code, which i think is a good thing.
> : But that's my style, and of course other people may disagree.
>
> : Now to my point:  Why does STL require this convention?  The STL code
> : can easily be written to never use the result of an assignment, right?
>
> Certainly.  But, should the international standard require all implementers
> to code the algorithms your way?  Or, is it reasonable to force you to
> return *this without forcing you to use the result?  IMHO, there is a
> compromise between your extreme and the nifty-code extreme which is
> clearer to almost everyone.

At the end of the day, there is only such much you can do to force
coding style.  The STL approach allows you to code using either
style and leaves it up to you to decide.  This, I believe, is a
good thing.


--
___________________________________________________________________
Marcelo Cantos, Research Assistant
Multimedia Database Systems Group              Tel: +61-3-9282-2497
723 Swanston St, Carlton VIC 3053, Australia   Fax: +61-3-9282-2490
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: David R Tribble <david.tribble@central.beasys.com>
Date: 1997/02/04
Raw View
Joe Keane wrote:
> > I'm rather dubious about the convention of having operators return one
> > of their arguments.  It's important to note that the result is entirely
> > syntactic sugar; it's semantically useless to return the first argument.

From: James Kuyper <kuyper@wizard.net> responded:
> Many operators, when applied to an object of one of the basic types,
> return that object, which can be passed on to further operators. In
> order to write analogous code using an overloaded operator, it must do
> the same thing.

Yeah, but this can all be done by returning a *const* reference.  The whole
point of this discussion was whether or not it's valid to return non-const
references from operator=() et al.

Allowing non-const references to be returned from operator=() allows such
nonsense as:

> >  class Foo  a, b, c;
> >  (a = b) = c;

Why this style of coding is better that the following is a mystery:

> >  a = b;
> >  a = c;

On the other hand, it's been pointed out that the STL *requires* classes
(of container template classes) to return non-const references.  Yuck.
Can't say this makes much sense to me.

-- David R. Tribble, david.tribble@central.beasys.com --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/01/29
Raw View
Herb Sutter (herbs@cntc.com) wrote:
: On 27 Jan 97 15:21:49 GMT, ark@research.att.com (Andrew Koenig) wrote:
: >> And BTW, operator=() should always return a const reference, period.
: >
: >No, it should return a plain reference.
: >
: >The reason is that a class whose operator=() returns a const reference
: >fails to meet the requirements for STL container elements.  You do want
: >to be able to create a vector, list, or set of objects of your class,
: >don't you?

: Yikes, you're right.  I have a problem with that.  Section 23.1
: [lib.container.requirements] Table 65 does indeed require:

:     expression:     t = u
:     return type:    T&
:     post-condition: t is equivalent to u

: but I can't offhand see any reason why this shouldn't read "T& or
: const T&" (or, to be a little more general, "(possibly cv-qualified)
: T&").  After all, if there is any specified code in other parts of the
: standard (say, in the iterator requirements) that uses the expression
: "t=u" as an lvalue -- which I doubt -- it can easily be rewritten in
: every case to avoid using "t=u" as an lvalue, right?  Or am I missing
: something?

: (I recently changed our coding standards to recommend returning only
: const& from operator=(), specifically to prevent the pathological
: "(A=B)=C".  It would be "most unfortunate" to switch back to T&.)

I remember an explaination (possibly D&E) that it was decided to have
operators which require a modifiable lvalue and return their new value
return a modifiable lvalue.  All of the built in assignment operators,
and prefix increment and decrement return modifiable lvalues.  It is
consistent and generally harmless.  It was suggested in another post
that the above pathological case should be fatal to the coder.  There
are lots of places in the language where rules were made to prevent
likely mistakes and surprises.  I doubt that the above code has much
chance of being written by accident.  Perhaps your coding standard
could be changed to require & with a note that (a = b) = c is cause
for unemployment;)

There are cases where the modifiable lvalue is useful.
 y = a * x * x + b * x + c
can be written as
 ((((y = a) *= x) += b) *= x) += c
which would prevent a number of expensive copies for a type like large
integer in the usual
 y = ((a * x) + b) * x + c
I find it easier to read than several lines
 y = a;
 y *= x;
 y += b;
 y *= x;
 y += c;
perhaps because I am used to things like
 strcat(strcat(strcpy(fileName, baseName), "."), extension);

I think that it is much more likely that something like
 ++ i %= size
would be written (it has been published as good code) to increment the
head or tail of a circular array queue.  If the type is int, that also
has undefined behavior due to two modifications of i without a sequence
point.

IMHO, if it is good enough for the ints which were debated by the
standards committee, it is good enough for my classes.  At this point
in time, I am much more interested in seeing the bugs removed from the
draft and a yes vote to raise it to DIS.  This point may be an
over statement which could be loosened but I see little harm in it.

John


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/01/30
Raw View
On 29 Jan 1997 21:28:41 GMT, jpotter@falcon.lhup.edu (John E. Potter)
wrote:
>I remember an explaination (possibly D&E) that it was decided to have
>operators which require a modifiable lvalue and return their new value
>return a modifiable lvalue.  All of the built in assignment operators,
>and prefix increment and decrement return modifiable lvalues.  It is
>consistent and generally harmless.  It was suggested in another post
>that the above pathological case should be fatal to the coder.  There
>are lots of places in the language where rules were made to prevent
>likely mistakes and surprises.  I doubt that the above code has much
>chance of being written by accident.  Perhaps your coding standard
>could be changed to require & with a note that (a = b) = c is cause
>for unemployment;)

I agree with the consistency and pink-slip arguments.  The behaviour of
code like "(A=B)=C" and other variants is "surprisingly subtle", however
(to borrow Murray's words), and I'd prefer to let the compiler prevent them
rather than attempt to prevent them with yet another coding standard rule.
And the language does in fact let me make the compiler prevent them, by
returning a const& from assignment... only to discover that the standard
library components rely (AFAICS, by specification only, and not by
implementation) on the looser/less-safe behaviour?

The people who would be most affected by the current language are those,
like me, who have been following the advice of C++ authors (at least as far
back as 1993, in Murray's book) to always return const& from assignment.
It means we have to go back and fix each such case if we want to be
compatible with the standard containers.  Not a big deal, necessarily, but
mildly annoying if you discover that you're in that category.  (And if you
are, it might take some time to figure out what the compiler diagnostic
means when you first try to use such a class in a container.)

[good examples elided]
>IMHO, if it is good enough for the ints which were debated by the
>standards committee, it is good enough for my classes.  At this point
>in time, I am much more interested in seeing the bugs removed from the
>draft and a yes vote to raise it to DIS.  This point may be an
>over statement which could be loosened but I see little harm in it.

Your "ints" argument is certainly convincing.  Still, if changing the
language to "T& or const T&" (or similar) required only cursory inspection
of other parts of the draft for consistency, I'd still prefer it in order
to avoid forcing some people to update their existing classes.  If it would
require more significant work, though, you're probably right that it's not
worth it at this point.

---
Herb Sutter (herbs@cntc.com)

Current Network Technologies Corp.
3100 Ridgeway, Suite 42, Mississauga ON Canada L5L 5M5
Tel 416-805-9088  Fax 905-608-2611


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Joe Keane <jgk@jgk.org>
Date: 1997/02/01
Raw View
I'm rather dubious about the convention of having operators return one
of their arguments.  It's important to note that the result is entirely
syntactic sugar; it's semantically useless to return the first argument.
If you're going to return a value, anything would be more useful.



Author: James Kuyper <kuyper@wizard.net>
Date: 1997/02/03
Raw View
Joe Keane wrote:
>
> I'm rather dubious about the convention of having operators return one
> of their arguments.  It's important to note that the result is entirely
> syntactic sugar; it's semantically useless to return the first argument.
> If you're going to return a value, anything would be more useful.
>
> From what i can see, the main beneficiaries of this convention are
> people that are in love with `look what i can do in one line' code:
>
>         (((taz *= yuk.ack()) >>= grom).blarf() += farg).mungex();
>
> Apparently many people think that this is incredibly nifty and clever;
> personally i start a quick search for my barf bag.  I hope that these
> people find Lisp so they can go away and do such things all they want.

The point of operator overloading is to allow you to write code using
user defined types that has the same syntax as analogous code using the
basic types. If your overloaded operator doesn't work in an analogous
way, you shouldn't be using a overloaded operator. Instead, you should
define a named member function. For example, if (A+B) != (B+A), then
either '+' or '!=' has probably been overloaded inappropriately.
Many operators, when applied to an object of one of the basic types,
return that object, which can be passed on to further operators. In
order to write analogous code using an overloaded operator, it must do
the same thing.


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: ark@research.att.com (Andrew Koenig)
Date: 1997/01/27
Raw View
In article <2.2.32.19970121223811.002cf574@central.beasys.com>
David R Tribble <david.tribble@central.beasys.com> writes:

> And BTW, operator=() should always return a const reference, period.

No, it should return a plain reference.

The reason is that a class whose operator=() returns a const reference
fails to meet the requirements for STL container elements.  You do want
to be able to create a vector, list, or set of objects of your class,
don't you?
--
    --Andrew Koenig
      ark@research.att.com
      http://www.research.att.com/info/ark
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/01/28
Raw View
On 27 Jan 97 15:21:49 GMT, ark@research.att.com (Andrew Koenig) wrote:
>> And BTW, operator=() should always return a const reference, period.
>
>No, it should return a plain reference.
>
>The reason is that a class whose operator=() returns a const reference
>fails to meet the requirements for STL container elements.  You do want
>to be able to create a vector, list, or set of objects of your class,
>don't you?

Yikes, you're right.  I have a problem with that.  Section 23.1
[lib.container.requirements] Table 65 does indeed require:

    expression:     t = u
    return type:    T&
    post-condition: t is equivalent to u

but I can't offhand see any reason why this shouldn't read "T& or
const T&" (or, to be a little more general, "(possibly cv-qualified)
T&").  After all, if there is any specified code in other parts of the
standard (say, in the iterator requirements) that uses the expression
"t=u" as an lvalue -- which I doubt -- it can easily be rewritten in
every case to avoid using "t=u" as an lvalue, right?  Or am I missing
something?

(I recently changed our coding standards to recommend returning only
const& from operator=(), specifically to prevent the pathological
"(A=B)=C".  It would be "most unfortunate" to switch back to T&.)

---
Herb Sutter (herbs@cntc.com)

Current Network Technologies Corp.
3100 Ridgeway, Suite 42, Mississauga ON Canada L5L 5M5
Tel 416-805-9088  Fax 905-608-2611


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/01/25
Raw View
James Kanze (james-albert.kanze@vx.cit.alcatel.fr) wrote:
: jpotter@falcon.lhup.edu (John E. Potter) writes:
: |>  herbs@cntc.com (Herb Sutter) wrote:
: |>  > On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
: |>  > >A colleague asked me whether:
: |>  > >        (A = B) = 5;
: |>  > >was defined, [...]
: |>  >   - always valid for classes A that use the default operator=, since
: |>  >     it returns an A&, not a const A&;
: |>  Correct.  Class X { };  X A, B, C;
: |>  (A = B) = C ---> A.operator=(B).operator=(C)
: |>  Which is well-formed and has defined behavior because function call and
: |>  return are both sequence points.  The side effects of A.operator=(B)
: |>  must be completed prior to the call A.operator=(C).  X::operator= is
: |>  a function defined by the compiler, not the global operator =.
: Is this true?  My impression is that the default operator= is, in fact,
: a "built-in", and thus, that no sequence point occurs.  But I am far
: from certain about this.

: Any other opinions?  (Preferably with citations from the draft to
: support them.)

Just unpacked my FCD.  Same opinion with citations:

  5.17 Assignment operators [expr.ass] /4
    If the left operand is of class type, the class shall be complete.
    Assignment to objects of a class is defined by the copy assignment
    operator (12.8, 13.5.3).
  12.8 Copying class objects [class.copy] /10
    If the class definition does not explicitly declare a copy assignment
    operator, one is declared implicitly.  ...  An implicitly-declared
    copy assignment operator is an inline public member of its class.

This is different from the ARM, but only the wording has changed from
CD1.  I have programs which perform differently under cfront when the
copy assignment operator is declared explicitely and does just the
memberwise copy.  The same for copy constructor.  It is likely that
your opinion would hold under the ARM, but I think the above shows a
change.  Anyone with a citation that I missed?



And in another subthread:
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
: jpotter@falcon.lhup.edu (John E. Potter) writes:
: |>  James Kanze (james-albert.kanze@vx.cit.alcatel.fr) wrote:
: |>  : herbs@cntc.com (Herb Sutter) writes:
: |>  : |>  On 15 Jan 1997, duncan@rcp.co.uk (Duncan Booth) wrote:
: |>  : |>  >A colleague asked me whether:
: |>  : |>  >        (A = B) = 5;
: |>  : |>  >was defined,
: |>  [snip]
: |>  : |>    - always valid for classes A that use the default operator=,
: |>  : |>      since it returns an A&, not a const A&;
: |>  : Interesting question.  I don't think that the default operator= counts
: |>  : as a function, in which case, it is invalid, as above (no intervening
: |>  : sequence points).  I could easily be wrong on this, although note that
: |>  : for PODS, the assignment operator does not introduce a sequence point
: |>  : in C.
: |>  : IMHO: the legality is determined by the operator= (and not by the type
: |>  : of A): the built-in operator= does not introduce a sequence point, so
: |>  : the operation is illegal.
: |>  Here we disagree.  My logic is:
: |>    operator= must be a member function.
: |>    if the class does not define an operator=, the implementation
: |>    must define it if it is used.
: |>    Conclusion:  It is a member function, ie function, sequence points.
: Can you take its address?  The only compiler on this machine (a C-Front
: derivative) doesn't allow it, but that really doesn't mean much.  I'll
: admit that I can find nothing in the standard that would forbid taking
: its address, but I'd feel better if the issue were addressed directly.
: (The fact that you can call the function explicitly is no real proof,
: since you can also call int::~int() explicitly.)

: I've just re-read the section in question in the Nov. 96 draft, and the
: current wording would seem to support your point of view.  In
: particular, it speaks of implicitly declaring and defining a public
: inline member.  (In fact, as it speaks of implicitly declaration
: separately from the implicite definition, one could potentially
: interpret the norme as meaning that I can provide a user-defined
: definition for the implicitly declared operator=, although such a
: definition would have to be inline.  I don't think that this was
: intended, but I could be wrong.)

Interesting.  Here is an opinion from g++2.7.2.  Not binding of course.

    #include <iostream.h:
    struct X {
        int x;
    //    X& operator= (X const&);
        };
    typedef X& (X::* Xptr)(X const&);
    /*
    X& X::operator= (X const& rhs) {
        x = rhs.x;
        return *this;
        }
    */
    int main () {
        Xptr p1(& X::operator=);
        X a = { 2 }, b = { 3 };
        cout << a.x << ' ' << b.x << '\n';
        (a.*p1)(b);
        cout << a.x << ' ' << b.x << '\n';
        }

Yes, I can take its address.  Yes, I can remove the C-style comment and
explicitely define the implicitely declared function.  Cfront, xlC (
cfront based) and Borland 4.5 choke on both of them.  Note the absence
of "inline" on my definition.  I agree with your read.  However, it
seems that one could forget the prototype in a header and define a
non-inline member in the implementation.  Clients would get the inline
implicit version.  That error should be a real joy to find.  I also
wonder what the intent was and if we have missed something.

: |>  Why do simple questions have such complex answers?
: Because this is C++.

Amen, of course, how stupid of me to ask.

Enjoy,
John
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Barry Margolin <barmar@bbnplanet.com>
Date: 1997/01/21
Raw View
Discussing the validity of (A = B) = C,

In article <rf5zpy47d37.fsf@vx.cit.alcatel.fr>,
James Kanze  <james-albert.kanze@vx.cit.alcatel.fr> wrote:
>There is one point on which I am not clear.  The built-in operator= does
>memberwise assignment.  If one of the members (or base classes) has a
>user-defined operator= (i.e. a function), then the built-in operator=
>will call this function.  Does this introduce a sequence point?  If the
>class of the above types does not have a user-defined operator=, but it
>contains just a single data member that does, would the above be well
>formed?

While I think that this member does introduce a sequence point, I don't
think it's sufficient to make the entire expression well-defined.  Suppose
the class has members x and y, where x has a user-defined operator= and y
doesn't.  The assignment is then effectively equivalent to one of

 ((A.x = B.x) = C.x, (A.y = B.y) = C.y)
or
 ((A.y = B.y) = C.y, (A.x = B.x) = C.x)

While the assignments to the x members are well-defined, the assignments to
the y members are not.

Actually, I think there are other equivalents, where the y assignments are
interleaved with the x assignments.  But my point still holds -- the
sequence point only makes the x assignments valid, but not the y's.
--
Barry Margolin
BBN Planet, Cambridge, MA
barmar@bbnplanet.com -  Phone (617) 873-3126 - Fax (617) 873-5508
(BBN customers, please call (800) 632-7638 option 1 for support)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/01/21
Raw View
James Kanze (james-albert.kanze@vx.cit.alcatel.fr) wrote:
: herbs@cntc.com (Herb Sutter) writes:

: |>  On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
: |>  >A colleague asked me whether:
: |>  >        (A = B) = 5;
: |>  >was defined,

[snip]

: |>    - always valid for classes A that use the default operator=, since
: |>      it returns an A&, not a const A&;

: Interesting question.  I don't think that the default operator= counts
: as a function, in which case, it is invalid, as above (no intervening
: sequence points).  I could easily be wrong on this, although note that
: for PODS, the assignment operator does not introduce a sequence point in
: C.

: IMHO: the legality is determined by the operator= (and not by the type
: of A): the built-in operator= does not introduce a sequence point, so
: the operation is illegal.

Here we disagree.  My logic is:
  operator= must be a member function.
  if the class does not define an operator=, the implementation
  must define it if it is used.
  Conclusion:  It is a member function, ie function, sequence points.

  A POD struct in C++ is still a class.

I once posed the question in this group of whether my defining the
operator= in the same way as the implementation default would make
any difference.  I was assured that it would not.

: |>    - always valid for classes A with a user-defined operator= that
: |>      returns an A&; and

: Correct.  The function call of operator= is a sequence point.

Here we agree, but the other stuff makes us both partially correct.
About all that can be said about a user defined operator= is that it
has arity 2 and is a member.
  R C::operator= (X rhs) [const] // I didn't think of const, thanks
It can return anything, take any parameter, and may be const.  Not much
can be said about (A = B) = C without one or more prototypes.

Why do simple questions have such complex answers?
John


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/01/21
Raw View
jpotter@falcon.lhup.edu (John E. Potter) writes:

|>  herbs@cntc.com (Herb Sutter) wrote:
|>  > On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
|>  > >A colleague asked me whether:
|>  > >        (A = B) = 5;
|>  > >was defined, [...]

    [...]
|>  >   - always valid for classes A that use the default operator=, since
|>  >     it returns an A&, not a const A&;
|>
|>  Correct.  Class X { };  X A, B, C;
|>  (A = B) = C ---> A.operator=(B).operator=(C)
|>  Which is well-formed and has defined behavior because function call and
|>  return are both sequence points.  The side effects of A.operator=(B)
|>  must be completed prior to the call A.operator=(C).  X::operator= is
|>  a function defined by the compiler, not the global operator =.

Is this true?  My impression is that the default operator= is, in fact,
a "built-in", and thus, that no sequence point occurs.  But I am far
from certain about this.

Any other opinions?  (Preferably with citations from the draft to
support them.)

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: David R Tribble <david.tribble@central.beasys.com>
Date: 1997/01/22
Raw View
|>  On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
|>  >A colleague asked me whether:
|>  >        (A = B) = 5;
|>  >was defined, ...

jpotter@falcon.lhup.edu (John E. Potter) comments:
>Why do simple questions have such complex answers?

On the contrary, there's a simple answer:  Don't do this.

Sure, it's intellectually stimulating to talk about how it could be done,
and what happens when you overload operator=(), and what if it returns a
object& instead of a const object&, but jeez, do we need this?

Anybody that codes like that should be shot.  It's counterintuitive, it
doesn't work for non-class types (like int), and at best it's confusing.

What are you trying to accomplish here?   Assign A the value of B and then
assign it the value 5?  Why not do this instead and be done with it:

    A = B;
    A = 5;

Anything more complicated that this should be coded and commented more
explicitly.

And BTW, operator=() should always return a const reference, period.

---

-- David R. Tribble, david.tribble@central.beasys.com --



[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/01/22
Raw View
jpotter@falcon.lhup.edu (John E. Potter) writes:

|>  James Kanze (james-albert.kanze@vx.cit.alcatel.fr) wrote:
|>  : herbs@cntc.com (Herb Sutter) writes:
|>
|>  : |>  On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
|>  : |>  >A colleague asked me whether:
|>  : |>  >        (A = B) = 5;
|>  : |>  >was defined,
|>
|>  [snip]
|>
|>  : |>    - always valid for classes A that use the default operator=, since
|>  : |>      it returns an A&, not a const A&;
|>
|>  : Interesting question.  I don't think that the default operator= counts
|>  : as a function, in which case, it is invalid, as above (no intervening
|>  : sequence points).  I could easily be wrong on this, although note that
|>  : for PODS, the assignment operator does not introduce a sequence point in
|>  : C.
|>
|>  : IMHO: the legality is determined by the operator= (and not by the type
|>  : of A): the built-in operator= does not introduce a sequence point, so
|>  : the operation is illegal.
|>
|>  Here we disagree.  My logic is:
|>    operator= must be a member function.
|>    if the class does not define an operator=, the implementation
|>    must define it if it is used.
|>    Conclusion:  It is a member function, ie function, sequence points.

Can you take its address?  The only compiler on this machine (a C-Front
derivative) doesn't allow it, but that really doesn't mean much.  I'll
admit that I can find nothing in the standard that would forbid taking
its address, but I'd feel better if the issue were addressed directly.
(The fact that you can call the function explicitly is no real proof,
since you can also call int::~int() explicitly.)

I've just re-read the section in question in the Nov. 96 draft, and the
current wording would seem to support your point of view.  In
particular, it speaks of implicitly declaring and defining a public
inline member.  (In fact, as it speaks of implicitly declaration
separately from the implicite definition, one could potentially
interpret the norme as meaning that I can provide a user-defined
definition for the implicitly declared operator=, although such a
definition would have to be inline.  I don't think that this was
intended, but I could be wrong.)

|>    A POD struct in C++ is still a class.

And so we get another difference between C and C++.  (Not an important
one; some programs that were undefined in C become defined in C++.)

|>  I once posed the question in this group of whether my defining the
|>  operator= in the same way as the implementation default would make
|>  any difference.  I was assured that it would not.

Which would also imply that you could take its address.

    [...]
|>  Why do simple questions have such complex answers?

Because this is C++.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/01/23
Raw View
David R Tribble <david.tribble@central.beasys.com> writes:

|>  |>  On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
|>  |>  >A colleague asked me whether:
|>  |>  >        (A = B) = 5;
|>  |>  >was defined, ...
|>
|>  jpotter@falcon.lhup.edu (John E. Potter) comments:
|>  >Why do simple questions have such complex answers?
|>
|>  On the contrary, there's a simple answer:  Don't do this.

That is definitly the answer I would give in
comp.lang.c++.moderated:-).  The purpose of this forum (comp.std.c++),
however, is to discuss what the standard says, and what is permitted (in
the absolute sense).  I'm sure that all of our readers understand that
not everything that is permitted is good programming practice.

    [...]
|>  And BTW, operator=() should always return a const reference, period.

Even in proxies?

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/01/18
Raw View
On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
>A colleague asked me whether:
>        (A = B) = 5;
>was defined, and after some discussion we concluded that it is undefined if
>the assigment operator is not overloaded, but defined if assignment to A has
>been overloaded. After reading chapter 5 again, I am no longer sure about this
>as I cannot find anything to say that the side effects must be completed
>before calling an overloaded operator.

This is a good and subtle question.  The following may not answer what
you're asking; apologies in advance if it's just review, or even
incorrect.  Corrections are welcome.

Here's a summary of my understanding (according to the ARM rules, I
don't think this has changed?), and assumes that it's legal to do the
assignment(s) in the first place (i.e., there is an A::operator=(const
B& or equivalent).  The simple case "(A=B)=literal" is:

  - always valid for builtins A, since "A=B" returns the lvalue
    "post-assignment A";
  - always valid for classes A that use the default operator=, since
    it returns an A&, not a const A&;
  - always valid for classes A with a user-defined operator= that
    returns an A&; and
  - never valid for classes A with a user-defined operator= that
    returns a const A&.

User-defined A::operator= can return other things, but it should
always return either A& or const A&.  It should in fact only ever be
const A& in practice, because of the possible aliasing in the more
general case "(A=B)=C", which is:

  - always undefined if A and C are aliases for the same object,
    because the order of argument evaluation for the second operator=
    is undefined; and
  - otherwise as above.

Having said all that, I still don't know whether I answered your
question :-), since you went on to ask specifically about side
effects.  In particular, if A (or B or C) are expressions instead of
just objects, then I'm as much in the dark as you are about the timing
of side effects.

>Chapter 5 of the DWP paragraph 2 says:
> "Operators  can  be  overloaded, that is, given meaning when applied to
>  expressions of class type (_class_) or enumeration type  (_dcl.enum_).
>  Uses  of  overloaded  operators are transformed into function calls as
>  described in _over.oper_.  Overloaded operators  obey  the  rules  for
>  syntax specified in this clause, but the requirements of operand type,
>  lvalue, and evaluation order are replaced by the  rules  for  function
>  call."
>
>It doesn't mention whether the side effects obey the rules for the operator or
>for the function call. I guess the bit about evaluation order is supposed to
>imply that side effects follow the function call rules, but paragraph 4 of the
>same section quite clearly separates order of evaluation and completion of
>side effects:
> "Except where noted, the order of evaluation of operands of  individual
>  operators  and subexpressions of individual expressions, and the order
>  in which side effects take place, is unspecified."
>
>Comments anyone?
>BTW, My view is that if assignment is overloaded, the result is defined (or
>would be if the wording was tightened up), but any programmer writing the
>above expression should be strung from the nearest lamppost.

If assignment is supplied but returns a const&, you're safe.  This is
good reason to always return const& from assignments -- it can save
you some rope.

---
Herb Sutter (herbs@cntc.com)

Current Network Technologies Corp.
3100 Ridgeway, Suite 42, Mississauga ON Canada L5L 5M5
Tel 416-805-9088  Fax 905-608-2611
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/01/20
Raw View
herbs@cntc.com (Herb Sutter) wrote:
> On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
> >A colleague asked me whether:
> >        (A = B) = 5;
> >was defined, and after some discussion we concluded that it is undefined
> >if the assigment operator is not overloaded, but defined if assignment
> >to A has been overloaded. After reading chapter 5 again, I am no longer
> >sure about this as I cannot find anything to say that the side effects
> >must be completed before calling an overloaded operator.

> This is a good and subtle question.  The following may not answer what
> you're asking; apologies in advance if it's just review, or even
> incorrect.  Corrections are welcome.

> Here's a summary of my understanding (according to the ARM rules, I
> don't think this has changed?), and assumes that it's legal to do the
> assignment(s) in the first place (i.e., there is an A::operator=(const
> B& or equivalent).  The simple case "(A=B)=literal" is:

>   - always valid for builtins A, since "A=B" returns the lvalue
>     "post-assignment A";

It is well formed with undefined behavior.  The key is sequence point,
see clause 1 basic definitions.  (A = B) = 5 changes A twice without
an interviening sequence point.  Syntactically valid (well formed)
but what happens is undefined.  No diagnostic required.

>   - always valid for classes A that use the default operator=, since
>     it returns an A&, not a const A&;

Correct.  Class X { };  X A, B, C;
(A = B) = C ---> A.operator=(B).operator=(C)
Which is well-formed and has defined behavior because function call and
return are both sequence points.  The side effects of A.operator=(B)
must be completed prior to the call A.operator=(C).  X::operator= is
a function defined by the compiler, not the global operator =.

>   - always valid for classes A with a user-defined operator= that
>     returns an A&; and

Correct, as above, provided the parameter is not a value.

>   - never valid for classes A with a user-defined operator= that
>     returns a const A&.

Correct.  Ill formed with required diagnostic for assignment to const.

> User-defined A::operator= can return other things, but it should
> always return either A& or const A&.  It should in fact only ever be
> const A& in practice, because of the possible aliasing in the more
> general case "(A=B)=C", which is:

>   - always undefined if A and C are aliases for the same object,
>     because the order of argument evaluation for the second operator=
>     is undefined; and

Wrong.  Sequence points assure the side effects happen and all
arguments are pointer or reference.  It makes no difference whether
the address of A (this) or the reference to C (argument alias to A) is
computed first, since they are the same and independent.  However, one
could define operator=() to take a value parameter.  In that case,
your arguement is valid.

> >Chapter 5 of the DWP paragraph 2 says:
> > "Operators  can  be  overloaded, that is, given meaning when applied to
> >  expressions of class type (_class_) or enumeration type  (_dcl.enum_).
> >  Uses  of  overloaded  operators are transformed into function calls as
> >  described in _over.oper_.  Overloaded operators  obey  the  rules  for
> >  syntax specified in this clause, but the requirements of operand type,
> >  lvalue, and evaluation order are replaced by the  rules  for  function
> >  call."
> >
> >It doesn't mention whether the side effects obey the rules for the
> >operator or for the function call. I guess the bit about evaluation
> >order is supposed to imply that side effects follow the function call
> >rules, but paragraph 4 of the same section quite clearly separates
> >order of evaluation and completion of side effects:
> > "Except where noted, the order of evaluation of operands of  individual
> >  operators  and subexpressions of individual expressions, and the order
> >  in which side effects take place, is unspecified."

See above on function call and sequence points.

> >Comments anyone?
> >BTW, My view is that if assignment is overloaded, the result is defined (or
> >would be if the wording was tightened up), but any programmer writing the
> >above expression should be strung from the nearest lamppost.

Perhaps, however,  (A = B) += C is a totally different story.

> If assignment is supplied but returns a const&, you're safe.  This is
> good reason to always return const& from assignments -- it can save
> you some rope.

And prevent the above optimization of A = B + C.  IMHO, there was much
more thought put into the operator= for builtins returning a nonconst
reference than there was in anyones recommendation for returning a
const reference.  When in doubt, do as the ints, they have been
debated by the standards committee.

John
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/01/20
Raw View
herbs@cntc.com (Herb Sutter) writes:

|>  On 15 Jan 1997 15:37:01 PST, duncan@rcp.co.uk (Duncan Booth) wrote:
|>  >A colleague asked me whether:
|>  >        (A = B) = 5;
|>  >was defined, and after some discussion we concluded that it is undefined
|>  >if the assigment operator is not overloaded, but defined if assignment
|>  >to A has been overloaded. After reading chapter 5 again, I am no longer
|>  >sure about this as I cannot find anything to say that the side effects
|>  >must be completed before calling an overloaded operator.
|>
|>  This is a good and subtle question.  The following may not answer what
|>  you're asking; apologies in advance if it's just review, or even
|>  incorrect.  Corrections are welcome.
|>
|>  Here's a summary of my understanding (according to the ARM rules, I
|>  don't think this has changed?), and assumes that it's legal to do the
|>  assignment(s) in the first place (i.e., there is an A::operator=(const
|>  B& or equivalent).  The simple case "(A=B)=literal" is:
|>
|>    - always valid for builtins A, since "A=B" returns the lvalue
|>      "post-assignment A";

Undefined behavior for basic types and pointers, since the expression
attempts to modify the same object (A) twice without an intervening
sequence point.

|>    - always valid for classes A that use the default operator=, since
|>      it returns an A&, not a const A&;

Interesting question.  I don't think that the default operator= counts
as a function, in which case, it is invalid, as above (no intervening
sequence points).  I could easily be wrong on this, although note that
for PODS, the assignment operator does not introduce a sequence point in
C.

IMHO: the legality is determined by the operator= (and not by the type
of A): the built-in operator= does not introduce a sequence point, so
the operation is illegal.

|>    - always valid for classes A with a user-defined operator= that
|>      returns an A&; and

Correct.  The function call of operator= is a sequence point.

|>    - never valid for classes A with a user-defined operator= that
|>      returns a const A&.

Except, of course, if the user-defined operator= is a const fucntion!
And never valid for classes A with a user-defined operator= that returns
void.

In general, IMHO, the expression is always illegal with the built-in
operator=.  Its legality for user-defined operator= depends whether it
is legal to assign C to the return value (which can, in principal, be
anything).  A user-defined operator= is a function, and calling it
introduces a sequence point.  All side-effects due to evaluating its
parameters must be finished before it is called, and all of the
side-effects which take place within it must be completed before it
returns.  (Note, however, that it is NOT specified whether "(A = B)" or
"C" will be evaluated first, only that both will be completely evaluated
before the final operator= is called.)

There is one point on which I am not clear.  The built-in operator= does
memberwise assignment.  If one of the members (or base classes) has a
user-defined operator= (i.e. a function), then the built-in operator=
will call this function.  Does this introduce a sequence point?  If the
class of the above types does not have a user-defined operator=, but it
contains just a single data member that does, would the above be well
formed?

|>  User-defined A::operator= can return other things, but it should
|>  always return either A& or const A&.  It should in fact only ever be
|>  const A& in practice, because of the possible aliasing in the more
|>  general case "(A=B)=C", which is:
|>
|>    - always undefined if A and C are aliases for the same object,
|>      because the order of argument evaluation for the second operator=
|>      is undefined; and

So?  If A::operator= is defined in the usual way, the actual parameters
will be references (aliasing or not); there is no way that C is going to
be moved.

|>    - otherwise as above.
|>
|>  Having said all that, I still don't know whether I answered your
|>  question :-), since you went on to ask specifically about side
|>  effects.  In particular, if A (or B or C) are expressions instead of
|>  just objects, then I'm as much in the dark as you are about the timing
|>  of side effects.

There are NO requirements concerning timing except those implied by
sequence points.  The relevant sequence points here would be:

    There is a sequence point at the completion of evaluation of each
    full-expression.

    When calling a function (whether or not the function is inline),
    there is a sequence point after the evaluation of all function
    arguments (if any) which takes place before execution of any
    expressions or statements in the function body.  There is also a
    sequence point after the copying of a returned value and before the
    execution of any expressions outside the function).  Several
    contexts in C++ cause evaluation of a function call, even though
    no corresponding function call syntax appears in the translation
    unit.

(The last line would seem to answer my question above.)

When in doubt, rewrite the expression using explicit function calls for
the user-defined operators.  Thus, for the traditional user-defined
operator= in the above expression:

    (A = B) = C ;
    (A.operator=( B )) = C ;
    (A.operator=( B )).operator=( C ) ;

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: duncan@rcp.co.uk (Duncan Booth)
Date: 1997/01/15
Raw View
A colleague asked me whether:
        (A = B) = 5;
was defined, and after some discussion we concluded that it is undefined if
the assigment operator is not overloaded, but defined if assignment to A has
been overloaded. After reading chapter 5 again, I am no longer sure about this
as I cannot find anything to say that the side effects must be completed
before calling an overloaded operator.

Chapter 5 of the DWP paragraph 2 says:
 "Operators  can  be  overloaded, that is, given meaning when applied to
  expressions of class type (_class_) or enumeration type  (_dcl.enum_).
  Uses  of  overloaded  operators are transformed into function calls as
  described in _over.oper_.  Overloaded operators  obey  the  rules  for
  syntax specified in this clause, but the requirements of operand type,
  lvalue, and evaluation order are replaced by the  rules  for  function
  call."

It doesn't mention whether the side effects obey the rules for the operator or
for the function call. I guess the bit about evaluation order is supposed to
imply that side effects follow the function call rules, but paragraph 4 of the
same section quite clearly separates order of evaluation and completion of
side effects:
 "Except where noted, the order of evaluation of operands of  individual
  operators  and subexpressions of individual expressions, and the order
  in which side effects take place, is unspecified."

Comments anyone?
BTW, My view is that if assignment is overloaded, the result is defined (or
would be if the wording was tightened up), but any programmer writing the
above expression should be strung from the nearest lamppost.

--
Duncan Booth                                             duncan@rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?
http://ourworld.compuserve.com/homepages/D_Booth
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]