Topic: Example in "The C++ Workbook" by Wiener and Pinson


Author: kcline@sun132.spd.dsccc.com (Kevin Cline)
Date: 1995/08/04
Raw View
In article <3vtat5INNovr@hubble.asymetrix.com>,
Michael Taylor <michaelt@asymetrix.com> wrote:
 >fenster@shadow.cs.columbia.edu (Sam Fenster) wrote:
 >>a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:
 >>> or do like that
 >>>
 >>>  inline template < class T > void swap(T& a, T& b)
 >>>  {
 >>>   a ^=b; b^=a;a ^=b;
 >>>  }
 >>
 >>This sets `a' and `b' to zero if they refer to the same object.
 >
 >Wrong!
 >
 >Let a = 1001110
 >and b = 1001110
 >
 >a ^= b; // -> a = 0
 >b ^= a; // -> b = 1001110
 >a ^= b; // -> a = 1001110
 >
 >
 >          Michael Taylor
 >     michaelt@asymetrix.com

Wrong! You have shown that it works if a and b have the same value.
If a and b REFER TO THE SAME OBJECT, (e.g. swap(A[i],A[j]) where i == j)
then a ^= b has the same effect as a ^= a; it sets a to zero.



--
Kevin Cline






Author: imp@village.org (Warner Losh)
Date: 1995/08/04
Raw View
In article <3vtat5INNovr@hubble.asymetrix.com>,
Michael Taylor <michaelt@asymetrix.com> wrote:
>fenster@shadow.cs.columbia.edu (Sam Fenster) wrote:
>>a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:
>>>   a ^=b; b^=a;a ^=b;
>>This sets `a' and `b' to zero if they refer to the same object.
>
>Wrong!
>
>Let a = 1001110
>and b = 1001110
>
>a ^= b; // -> a = 0
>b ^= a; // -> b = 1001110
>a ^= b; // -> a = 1001110

No.  If a and be are the same object then you'll get

a ^= a // (a = 0);
a ^= a // (a = 0);
a ^= a // (a = 0);

Which is incorrect!

Notice he didn't say same value, he said same object.

It also doesn't work if ^= isn't defined on the type.

Warner


--
Warner Losh  "VMS Forever"  home: imp@village.org
Cyberspace Development, Inc   work: imp@marketplace.com
Makers of TIA, The Internet Adapter.  http://marketplace.com/





Author: michaelt@asymetrix.com (Michael Taylor)
Date: 1995/08/04
Raw View
fenster@shadow.cs.columbia.edu (Sam Fenster) wrote:
>a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:
>> or do like that
>>
>>  inline template < class T > void swap(T& a, T& b)
>>  {
>>   a ^=b; b^=a;a ^=b;
>>  }
>
>This sets `a' and `b' to zero if they refer to the same object.

Wrong!

Let a = 1001110
and b = 1001110

a ^= b; // -> a = 0
b ^= a; // -> b = 1001110
a ^= b; // -> a = 1001110


          Michael Taylor
     michaelt@asymetrix.com





Author: ark@research.att.com (Andrew Koenig)
Date: 1995/08/01
Raw View
In article <3vj9ak$49m@news.rrz.uni-koeln.de> a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:

> or do like that

>  inline template < class T > void swap(T& a, T& b)
>  {
>   a ^=b; b^=a;a ^=b;
>  }

... which fails when you try to swap floating-point values, for example.
--
    --Andrew Koenig
      ark@research.att.com





Author: fenster@shadow.cs.columbia.edu (Sam Fenster)
Date: 1995/07/31
Raw View
>> template < class T >
>> void swap(T & item1, T & item2)
>> {
>>  T temp = item1;
>>  item1 = item2;
>>  item2 = temp;
>> }

a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:
> or do like that
>
>  inline template < class T > void swap(T& a, T& b)
>  {
>   a ^=b; b^=a;a ^=b;
>  }

This sets `a' and `b' to zero if they refer to the same object.  You might do
this inadvertently if you were swapping array elements, for instance.

Also, `operator ^= (T &, T &)' is not defined for arbitrary types,
e.g. classes.





Author: a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl)
Date: 1995/07/31
Raw View
> template < class T >
> void swap(T & item1, T & item2)
> {
>  T temp = item1;
>  item1 = item2;
>  item2 = temp;
> }

or do like that

 inline template < class T > void swap(T& a, T& b)
 {
  a ^=b; b^=a;a ^=b;
 }







Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/07/31
Raw View
a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:

>> template < class T >
>> void swap(T & item1, T & item2)
>> {
>>  T temp = item1;
>>  item1 = item2;
>>  item2 = temp;
>> }

>or do like that

> inline template < class T > void swap(T& a, T& b)
> {
>  a ^=b; b^=a;a ^=b;
> }

I had hoped that this old idea was dead, but I guess it isn't.

First of all, the code will compile only if operator^= is available.
For built-in types, this is true only for the integer types. You
can't swap floats, doubles, or pointers, for example.

Secondly, it doesn't always give the correct answer. Here is
a simple test case:

 int i = 2;
 cout << i << endl; // prints '2'
 swap(i, i);
 cout << i << endl; // prints '0'

The original template works for all types which have assignment
defined. Every type can be assigned unless you take steps to
prevent it.
--
Steve Clamage, stephen.clamage@eng.sun.com





Author: sartin@pencom.com (Rob Sartin)
Date: 1995/08/01
Raw View
[Followups to comp.lang.c++, this is off-topic for comp.std.c++.]

In article <3vjkij$ctk@engnews2.Eng.Sun.COM>,
Steve Clamage <clamage@Eng.Sun.COM> wrote:
>a2675528@athena.rrz.Uni-Koeln.DE (Paul Sponagl) writes:
>
>>>     template < class T >
>>>     void swap(T & item1, T & item2)
>>>     {
>>>             T temp = item1;
>>>             item1 = item2;
>>>             item2 = temp;
>>>     }
>>or do like that
>> inline template < class T > void swap(T& a, T& b)
>> {
>>  a ^=b; b^=a;a ^=b;
>> }
>
>I had hoped that this old idea was dead, but I guess it isn't.

Ditto.

Steve gives two reasons why it's bad (only works if operator^=
defined and acts as assumed, doesn't always work even then).

There is at least one more: compiler writers often don't optimize
for this old hack (which worked better than a swap with compilers
that did no optimization). Even for "int" I get worse code on two
modern compilers for the xor hack. Sounds like another example of
a traditional hack whose useful life has past (cf. a recent
comp.compilers discussion on bit counting).

GNU g++ on NeXT generally yields a load address and three move
instructions (memory to register, memory to memory, register to
memory) for the temporary version.  For the xor version GNU g++ on
NeXT generally yields a load address, three moves (all memory to
register), and three xor instructions (all with a target in memory).

C/Set++ 3.1.2 on AIX 4.1.3 generally yields two loads and two stores
for the temporary version and three load, three xors, and three
stores for the xor version.

So in addition to having correctness and extensibility problems,
"XOR swapping" turns out to be a poor efficiency hack as well.

Regards,

Rob





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/07/27
Raw View
kirill@sawnlk.cs.ualberta.ca (Kirill Richine) writes:

>The book "The C++ Workbook" by Richard S. Wiener and Lewis J. Pinson
>(Addison-Wesley) gives the following example
>
>#include <stdio.h>
>
>void swap(void *&item1,void *&item2)
>{
>  void *temp=item1;
>
>  item1=item2;
>  item2=temp;
>}
>
>main()
>{
>  int *i=new int;
>  int *j=new int;
>
>  *i=5;
>  *j=20;
>  swap(i,j);
>  printf("*i=%d *j=%d\n",*i,*j);
>
>  float *x=new float;
>  float *y=new float;
>
>  *x=5.0;
>  *y=20.0;
>  swap(x,y);
>  printf("*x=%f *y=%f\n",*x,*y);
>}
>
>
>The compiler gives the following warnings:
>
>"swap.cc", line 19: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
>"swap.cc", line 19: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
>"swap.cc", line 27: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
>"swap.cc", line 27: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
>
>and the actual swapping, does not happen.
>Can you show me where the bug is?

The bug is attempting to use that swap() function to swap pointers to
int or float rather than pointers to void.  As written, the swap()
function will only work for pointers to void.  The reason it only
works for pointers to void is that other sorts of pointers might
be repesented differently on different machines.  For example, on
a word-addressed machine, a pointer to int might occupy one word,
but a pointer to void might need two words.  You can't assign to
a pointer to int via a reference to a pointer to void.

If you want it to work for all types, you need to use a template:

 template < class T >
 void swap(T & item1, T & item2)
 {
  T temp = item1;
  item1 = item2;
  item2 = temp;
 }

--
Fergus Henderson              | Designing grand concepts is fun;
fjh@cs.mu.oz.au               | finding nitty little bugs is just work.
http://www.cs.mu.oz.au/~fjh   | -- Brooks, in "The Mythical Man-Month".
PGP key fingerprint: 00 D7 A2 27 65 09 B6 AC  8B 3E 0F 01 E7 5D C4 3F





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/07/28
Raw View
kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:

>ark@research.att.com (Andrew Koenig) writes:
>
>|> so this shouldn't work.  What should happen is that the compiler should
>|> refuse to execute your program,
>
>Since this is comp.std.c++ (and not comp.lang.c++): does the standard
>say that the implementation must refuse to execute your program.

No.

>It certainly requires a diagnostic, but it was my understanding that once
>the diagnostic was emitted, the implementation was free to do pretty
>much whatever it pleased (including generating executable code which
>does something completely different than what the programmer wanted).

That's correct.

--
Fergus Henderson              | Designing grand concepts is fun;
fjh@cs.mu.oz.au               | finding nitty little bugs is just work.
http://www.cs.mu.oz.au/~fjh   | -- Brooks, in "The Mythical Man-Month".
PGP key fingerprint: 00 D7 A2 27 65 09 B6 AC  8B 3E 0F 01 E7 5D C4 3F





Author: kirill@sawnlk.cs.ualberta.ca (Kirill Richine)
Date: 1995/07/19
Raw View
Hi!

The book "The C++ Workbook" by Richard S. Wiener and Lewis J. Pinson
(Addison-Wesley) gives the following example

#include <stdio.h>

void swap(void *&item1,void *&item2)
{
  void *temp=item1;

  item1=item2;
  item2=temp;
}


main()
{
  int *i=new int;
  int *j=new int;

  *i=5;
  *j=20;
  swap(i,j);
  printf("*i=%d *j=%d\n",*i,*j);

  float *x=new float;
  float *y=new float;

  *x=5.0;
  *y=20.0;
  swap(x,y);
  printf("*x=%f *y=%f\n",*x,*y);
}


The compiler gives the following warnings:

"swap.cc", line 19: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
"swap.cc", line 19: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
"swap.cc", line 27: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)
"swap.cc", line 27: warning: temporary used for non-const void *& argument; no changes will be propagated to actual argument (anachronism)

and the actual swapping, does not happen.
Can you show me where the bug is?

Thank you.
k&






Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1995/07/20
Raw View
In article <DBzuD7.BGq@research.att.com> ark@research.att.com (Andrew
Koenig) writes:

|> so this shouldn't work.  What should happen is that the compiler should
|> refuse to execute your program,

Since this is comp.std.c++ (and not comp.lang.c++): does the standard
say that the implementation must refuse to execute your program.  It
certainly requires a diagnostic, but it was my understanding that once
the diagnostic was emitted, the implementation was free to do pretty
much whatever it pleased (including generating executable code which
does something completely different than what the programmer wanted).
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils en informatique industrielle --
                              -- Beratung in industrieller Datenverarbeitung







Author: ark@research.att.com (Andrew Koenig)
Date: 1995/07/20
Raw View
In article <3ujj8d$o4m@scapa.cs.ualberta.ca> kirill@sawnlk.cs.ualberta.ca (Kirill Richine) writes:

> The book "The C++ Workbook" by Richard S. Wiener and Lewis J. Pinson
> (Addison-Wesley) gives the following example

> #include <stdio.h>

> void swap(void *&item1,void *&item2)
> {
>   void *temp=item1;
>
>   item1=item2;
>   item2=temp;
> }

OK ... so this function should swap two pointers to void...

> main()
> {
>   int *i=new int;
>   int *j=new int;

but i and j aren't pointers to void, they're pointers to int...

>   *i=5;
>   *j=20;
>   swap(i,j);

so this shouldn't work.  What should happen is that the compiler should
refuse to execute your program, but once upon a time (i.e. more than five years
ago) it used to be legal to bind a reference to an lvalue of the wrong type
and the compiler would quietly create a temporary -- so your compiler merely
gives a warning instead of an error.

>   printf("*i=%d *j=%d\n",*i,*j);

>   float *x=new float;
>   float *y=new float;

>   *x=5.0;
>   *y=20.0;
>   swap(x,y);

Exactly the same problem here, of course.

>   printf("*x=%f *y=%f\n",*x,*y);
> }

A much better way to write swap:

 template<class T> void swap (T& x, T& y)
 {
  T t = x;
  x = y;
  y = t;
 }
--
    --Andrew Koenig
      ark@research.att.com