Topic: assignment operators and const members or references
Author: boukanov@sentef2.fi.uib.no (Igor Boukanov)
Date: 1997/03/28 Raw View
Hi,
Dietmar Kuehl (kuehl@uzwil.informatik.uni-konstanz.de) wrote:
> Your reasons outlined below are *NOT* reasons why it might be
> reasonable to do *assignment* to constant members or referernce members.
> These are reasons why the objects should be constructed!
Here an example that has to use the trick.
Consider a class A that use for the implementation purpose a member of
type T that does not have accessible copy operator but has well defined
destroy/create semantic.
Definitely it would be natural to write:
class A {
...
private:
T t;
...
public:
A& operator=(const A& a) {
if (this != &a) {
....
t.~T();
new (&t) T(a.t);
...
}
return *this;
}
...
}
(It not necessary to check here for '&t != &a.t' because the check
'this != &a' guaranty that.)
I do not see any problems that the solution can cause and it seems
it is the only way to write ' operator=' at all for A.
--
Regards, Igor Boukanov.
igor.boukanov@fi.uib.no
http://www.fi.uib.no/~boukanov/
---
[ 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: Bob Sidebotham <rns@fore.com>
Date: 1997/03/21 Raw View
No doubt this has been hashed over in the past. But I'd appreciate any
tips that anyone might have.
I have a class like:
class foo {
bar& b;
};
and I'd like to be able to write an assignment operator for it.
Unfortunately, the default assignment operator will not be generated,
since it is defined as member wise assignment, and b is not assignable.
This seems bogus to me: I understand that a reference is final, and
can't be changed, but it seems to me that in the special situation of
assignment, you are really constructing, effectively, a completely new
copy of the object.
The only way I have found to write the assignment operator is as:
foo::foo& operator=(const foo& xxx) {
memcpy((void *) this, (void *) &xxx, sizeof(xxx));
return *this;
}
which is highly unsatisfactory, although I would guess it should be
reasonably portable--although perhaps not: is the compiler free to
assume that reference and const members *never* change, even after
assignment of the whole structure?
Comments?
Thanks,
Bob Sidebotham
---
[ 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: Marcelo Cantos <marcelo@mds.rmit.edu.au>
Date: 1997/03/22 Raw View
Bob Sidebotham <rns@fore.com> writes:
>
> No doubt this has been hashed over in the past. But I'd appreciate any
> tips that anyone might have.
>
> I have a class like:
>
> class foo {
> bar& b;
> };
>
> and I'd like to be able to write an assignment operator for it.
>
> This seems bogus to me: I understand that a reference is final, and
> can't be changed, but it seems to me that in the special situation of
> assignment, you are really constructing, effectively, a completely new
> copy of the object.
This is a classic case of when *not* to use a reference member!
A reference variable can only be assigned once, this is its very
nature. The same holds for any class that contains a reference
member.
> The only way I have found to write the assignment operator is as:
>
> foo::foo& operator=(const foo& xxx) {
> memcpy((void *) this, (void *) &xxx, sizeof(xxx));
> return *this;
> }
Children, PLEASE DO NOT TRY THIS AT HOME!!!
> which is highly unsatisfactory, although I would guess it should be
> reasonably portable--although perhaps not: is the compiler free to
> assume that reference and const members *never* change, even after
> assignment of the whole structure?
No. The above code is not portable, since the language stipulates
that reference variables can never be modified. This means that
compilers are free to optimise reference member access by leaving it
in a register. So overwriting the class with memcpy will not always
work.
This code is completely opposed to the spirit of C++ and is very
dangerous. Use a pointer!
--
______________________________________________________________________
Marcelo Cantos, Research Assistant __/_ marcelo@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT / _ Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053 Aus/ralia ><_> Fax 61-3-9282-2490
Acknowledgements: errors - me; wisdom - God; funding - RMIT
---
[ 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/03/22 Raw View
Bob Sidebotham <rns@fore.com> wrote:
> class foo {
> bar& b;
> };
<snip>
>The only way I have found to write the assignment operator is as:
>
> foo::foo& operator=(const foo& xxx) {
> memcpy((void *) this, (void *) &xxx, sizeof(xxx));
> return *this;
> }
>
>which is highly unsatisfactory, although I would guess it should be
>reasonably portable--although perhaps not:
Yikes, urk, egad! This isn't just unsatisfactory: it's nonportable,
nonstandard, and bordering on pure evil and probably high cholesterol. Don't
do it. You can't rebind references. You just can't. It's impossible. Use a
pointer member variable instead of the reference member variable.
>is the compiler free to
>assume that reference and const members *never* change, even after
>assignment of the whole structure?
Yes, because the language guarantees it! :-) Dirty tricks like the above
notwithstanding, since they aren't legal C++.
---
Herb Sutter (mailto:herbs@cntc.com)
Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824
---
[ 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: boukanov@sentef2.fi.uib.no (Igor Boukanov)
Date: 1997/03/22 Raw View
Bob Sidebotham (rns@fore.com) wrote:
> The only way I have found to write the assignment operator is as:
> foo::foo& operator=(const foo& xxx) {
> memcpy((void *) this, (void *) &xxx, sizeof(xxx));
> return *this;
> }
The better idea is to write,
foo::foo& operator=(const foo& xxx) {
this->~foo();
new (this) foo(xxx);
return *this;
}
It seems this is more portable. There is even an example of code like
this in CD2.
BTW, in ADA95 this is the default behaviour of assignment.
--
Regards, Igor Boukanov.
igor.boukanov@fi.uib.no
http://www.fi.uib.no/~boukanov/
---
[ 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: kuehl@uzwil.informatik.uni-konstanz.de (Dietmar Kuehl)
Date: 1997/03/22 Raw View
Hi,
Bob Sidebotham (rns@fore.com) wrote:
: I have a class like:
: class foo {
: bar& b;
: };
: and I'd like to be able to write an assignment operator for it.
Judging from the rest of your article, your main point about the
assignment is that you want to reseat 'b'. Otherwise, just write an
assignment operator...
: Unfortunately, the default assignment operator will not be generated,
: since it is defined as member wise assignment, and b is not assignable.
Exactly. In fact, the compiler is not even required to generate any
representation for 'b' in objects of the class 'foo' anyway, if it can
keep track what object is referenced with some other mechanism
(although it is unlikely that it can, I think). Thus, there is no
[portable] way to change 'b' to reference another object. The simple
approach to avoid this problem is to use a pointer to 'bar' instead:
Only the sytnax used within the implementation of 'foo' changes.
Probably, even identical code is generated.
: This seems bogus to me: I understand that a reference is final, and
: can't be changed, but it seems to me that in the special situation of
: assignment, you are really constructing, effectively, a completely new
: copy of the object.
This may hold for some classes but may not hold for others. The
"normal" sematics of assignment is that the left hand side object of
the assignment represents the same value as the right hand side.
Whether this mimicks destruction followed by a copy construction or
does something different, which might even depend on the former value
of the left hand side, is completely up to the class and I use both
approaches.
: The only way I have found to write the assignment operator is as:
: foo::foo& operator=(const foo& xxx) {
: memcpy((void *) this, (void *) &xxx, sizeof(xxx));
: return *this;
: }
This code is only portable if the class 'foo' is a POD (plain ol' data)
but a POD can definitely not contain a reference (class paragraph 4 of
the Dec '96 DWP).
: which is highly unsatisfactory, although I would guess it should be
: reasonably portable--although perhaps not: is the compiler free to
: assume that reference and const members *never* change, even after
: assignment of the whole structure?
Yes, the compiler can assume that the reference stays unchanged after
initialization until destruction. This is a fundamental property of all
references, whether they are members of class or other references.
: Comments?
Why don't you use a pointer to the object being referenced instead?
--
<mailto:dietmar.kuehl@uni-konstanz.de>
<http://www.informatik.uni-konstanz.de/~kuehl/>
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ 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: Gerard Weatherby <gerardw@alum.mit.edu>
Date: 1997/03/22 Raw View
Bob Sidebotham wrote:
> I have a class like:
>
> class foo {
> bar& b;
> };
>
> and I'd like to be able to write an assignment operator for it.
> Unfortunately, the default assignment operator will not be generated,
> since it is defined as member wise assignment, and b is not assignable.
>
> This seems bogus to me: I understand that a reference is final, and
> can't be changed, but it seems to me that in the special situation of
> assignment, you are really constructing, effectively, a completely new
> copy of the object.
Assignment is not construction. As you note, a member reference can't
be changed after construction--that's how the language is defined,
(proposed, at least).
You can get the functionality within the existing rules.
class foo {
public:
foo(bar &b):pB(&b) {}
foo & operator=(const foo &rhs)
{ pB = rhs.pB; }
private:
bar & B( )
{ return *pB; }
bar *pB;
};
bar
---
[ 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: smeyers@teleport.com (Scott Meyers)
Date: 1997/03/23 Raw View
In article <5h0sdc$r9b$1@toralf.uib.no>,
| The better idea is to write,
| foo::foo& operator=(const foo& xxx) {
| this->~foo();
| new (this) foo(xxx);
| return *this;
| }
Note that this may change the type of *this, because the use
of placement new will reinitialize its vptr.
Scott
--
Scott Meyers, Ph.D. Voice: 503/638-6028
C++ Consulting and Training Fax: 503/638-6614
Author of "Effective C++" Email: smeyers@netcom.com
and "More Effective C++" WWW: http://www.teleport.com/~smeyers
---
[ 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/03/23 Raw View
boukanov@sentef2.fi.uib.no (Igor Boukanov) wrote:
>The better idea is to write,
> foo::foo& operator=(const foo& xxx) {
> this->~foo();
> new (this) foo(xxx);
> return *this;
> }
>
>It seems this is more portable.
Egad, yikes, no! Ignoring for now the nit that it won't deal with
self-assignment:
1. The design is wrong because copy and assignment semantics are not the
same... this is inefficient at best and wrong (because of slicing) at worst.
2. The approach is wrong because you're trying to cure the symptom, not the
disease. In this case the solution isn't to work around the reference member,
it's to get rid of the reference member in the first place since it should be
a pointer member.
Hence the passage in our internal coding standards (note the part about "even
though this trick crops up every three months on the newsgroups" :-) ):
- prefer writing a common private function to share code between
copying and copy assignment, if necessary; never use the trick
of implementing copy assignment in terms of copy construction
by using an explicit destructor followed by placement new, even
though this trick crops up every three months on the newsgroups
(i.e., never write "T& T::operator=( const T& other )
{
if( this != &other)
{
this->~T(); // evil
new (this) T( other ); // evil
}
return *this;
}")
>There is even an example of code like this in CD2.
Could you point out the reference?
---
Herb Sutter (mailto:herbs@cntc.com)
Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824
---
[ 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: presto@haggard.gg.caltech.edu (Preston Pfarner)
Date: 1997/03/23 Raw View
In article <5h0sdc$r9b$1@toralf.uib.no> boukanov@sentef2.fi.uib.no
(Igor Boukanov) writes:
> The better idea is to write,
> foo::foo& operator=(const foo& xxx) {
> this->~foo();
> new (this) foo(xxx);
> return *this;
> }
>
> It seems this is more portable. There is even an example of code like
> this in CD2.
>
> BTW, in ADA95 this is the default behaviour of assignment.
If you choose to use this approach, please change it slightly:
foo::foo& operator=(const foo& xxx) {
if (this != &xxx) {
this->~foo();
new (this) foo(xxx);
}
return *this;
}
Without this change, "x = x;" will fail, since you destroy x and then
read from it (to load itself, even).
Keep in mind that this means that all members will be destroyed and
recreated, so any heap objects may move. If you have any pointers
(or references) into a member on the heap, they become invalid with
this method.
--
Preston Pfarner
mailto:presto@gg.caltech.edu
http://www.gg.caltech.edu/~presto/
---
[ 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: boukanov@sentef2.fi.uib.no (Igor Boukanov)
Date: 1997/03/24 Raw View
Herb Sutter (herbs@cntc.com) wrote:
> >There is even an example of code like this in CD2.
> Could you point out the reference?
Look at 3.8 in the CD2. There are a lot of examples with code like this. There
is even the example for the assignment operator in 3.8.7 which is not evil
(undefined behavior).
--
Regards, Igor Boukanov.
igor.boukanov@fi.uib.no
http://www.fi.uib.no/~boukanov/
---
[ 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: "Risto Lankinen" <rlankine@us.oracle.com>
Date: 1997/03/24 Raw View
Hi!
Bob Sidebotham <rns@fore.com> wrote in article
<33330902.41C67EA6@fore.com>...
>
> I have a class like:
>
> class foo {
> bar& b;
> };
>
> and I'd like to be able to write an assignment operator for it.
> Unfortunately, the default assignment operator will not be generated,
> since it is defined as member wise assignment, and b is not assignable.
Do the following two steps:
1. Implement the assignment using explicit call to destructor and
copy constructor (to explicitly call copy constructor, use the
placement syntax of 'operator new()' ).
2. Make sure the copy constructor reinitializes the reference the
way you want it to.
Example:
foo &foo::operator=( const foo &r )
{
if( this != &r )
{
this->~foo();
new( this ) foo( r );
}
return *this;
}
foo::foo( const foo &r ) : b(r.b)
{
}
terv: Risto Lankinen
---
[ 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: Bob Sidebotham <rns@fore.com>
Date: 1997/03/25 Raw View
Thanks for the replies on this one. Just to put this in perspective, I
think there are legitimate reasons that one might want constant members
or references and still be able to do structure assignment:
1. Since array initialization is so limited, it is essentially
impossible (in many cases) to allocate an array of structures that have
constant members or references. What I really want to do, in this case,
is to allocate the entire array without initialization, and then for
each element copy in a newly constructed value. But I can't, because C++
doesn't allow me to express the delayed construction, since it requires
assignment. I can change the code to allocate an array of pointers, and
then allocate each element separately, but there's a performance issue
associated with doing this.
2. STL containers basically won't work with structures of this type,
since they require an assignment operator to implement push_back. Of
course, I can always use pointers here, too (that is, create a container
of pointers, rather than structures), and this is OK, but yet another
restriction that the C++ user has to learn (or discover the hard way).
3. It's been suggested that I just use a pointer within the structure,
rather than a reference. But there's legitimate reasons to want to
record the fact that the reference is constant. I suppose I could
redefine the reference as a private data member (pointer), and then
provide an access function that returns a reference. But this is jumping
through hoops to express something that is very simple. I might be
happier if C++ allowed me to invoke member functions as if they
variables (i.e. with no parentheses required), so that, to the caller,
they were indistinguishable. Then I could more easily change the
representation.
Thanks,
Bob Sidebotham
---
[ 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: Pete Becker <pbecker@openenv.com>
Date: 1997/03/25 Raw View
Herb Sutter wrote:
>
> boukanov@sentef2.fi.uib.no (Igor Boukanov) wrote:
> >The better idea is to write,
> > foo::foo& operator=(const foo& xxx) {
> > this->~foo();
> > new (this) foo(xxx);
> > return *this;
> > }
>
> >There is even an example of code like this in CD2.
>
> Could you point out the reference?
>
It's there, in the context of the description of the object model. It is
not there as an example of how to write an assignment operator. In fact,
if you use it, you must use the same trick in any class that is derived
from this class. It's nasty and ugly. Don't do it.
-- Pete
---
[ 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: kuehl@uzwil.informatik.uni-konstanz.de (Dietmar Kuehl)
Date: 1997/03/25 Raw View
Hi,
Bob Sidebotham (rns@fore.com) wrote:
: Thanks for the replies on this one. Just to put this in perspective, I
: think there are legitimate reasons that one might want constant members
: or references and still be able to do structure assignment:
Your reasons outlined below are *NOT* reasons why it might be
reasonable to do *assignment* to constant members or referernce members.
These are reasons why the objects should be constructed!
: 1. Since array initialization is so limited, it is essentially
: impossible (in many cases) to allocate an array of structures that have
: constant members or references.
It is possible in C++ to allocate an array and initialize every element
of the array separately. This is just not possible with the indeed
very limited built-in arrays. Thus, and this is what I recomment
anyway, do not use built-in arrays! Reasonable uses of built-in arrays
are basically limited to the implementation of low-level classes, like
eg. array classes (although even there it is only taken advantage of
the built-in 'operator[]()' for pointers; objects are constructed and
destructed by placement new and explicit destruction in "raw memory").
Any reasonable array class will have the possibility to construct
objects in-place either from a prototype object or from a sequence. In
addition, it may be possible to add individual elements.
: What I really want to do, in this case,
: is to allocate the entire array without initialization, and then for
: each element copy in a newly constructed value. But I can't, because C++
: doesn't allow me to express the delayed construction, since it requires
: assignment.
Not true! What can be done, and what is done by reasonable array
classes, is to allocate a chunk of uninitialized memory followed by
placement construction of the individual array elements (a commented
example, although probably not up to date with my current
implementations techniques, can be found at
<http://www.informatik.uni-konstanz.de/~kuehl/c++/array.h.html>).
Whether you can construct the object directly in-place our if you have
to create an object first which is just copied, depends on the array
class (normally the second approach is used, since the former approach
requires knowledge of the type stored in the array to provide the
necessary interface).
: 2. STL containers basically won't work with structures of this type,
: since they require an assignment operator to implement push_back.
'cont.push_back(x)' is defined as (that is, to have the effect of),
'cont.insert(cont.end(), x)'. The 'insert()' method has to potentially
move the objects in the container and then construct a new element.
Although all implementations of STL I have available implement the
'insert()' method to move elements using assignments instead of a
sequence of copy and destruction pairs, I don't think that this is
required. Thus, I don't think that STL by the definition as found in
the DWP makes it impossible to put elements of a type without an
assigment operator into a container like 'deque' or 'vector'.
Also, I don't think that the implementation has to follow literally the
approach stated in the DWP: I think, the implementation is free to
implement 'push_back()' differently as long as the effect on the
container is the same (or is it really necessary that
'cont.push_back(x)' calls 'cont.insert(cont.begin(), x)'?). For example
to the HP/SGI implementation of 'vector' this would mean, that some
checks can be avoided: If 'push_back()' is used, it is known in advance
that the new object is put at the end of the 'vector' and there is no
need to determine whether other objects have to be moved.
: 3. It's been suggested that I just use a pointer within the structure,
: rather than a reference. But there's legitimate reasons to want to
: record the fact that the reference is constant.
Yes, it is legitimate to record the fact that the reference is
constant. But what you are asking for is to record the fact that a
reference is constant except sometimes, where it is not constant. Thus,
the recorded fact that it is constant is violated. I think, it is thus
unreasonable to record the fact that the reference is constant, since
this fact is false!
If your problem is really that you want to have an array where objects
with constant member are to be stored and this is the reason why you
want to modify the constant members, then I strongly suggest that you
use an array class which constructs the elements in place. It is not
that hard to write, altough there are some tricky parts to it. This
would solve the problem in a clean and efficient way.
Finally, a note on the use of placement new and explicit destruction:
Actually, the 'allocator' class of the standard defines the necessary
member to encapsulate both memory allocation and construction of
objects (and corresponding for the deallocation and destruction). Thus,
an array class should probably rather use an 'allocator' than direct
calls to placement new etc (this is done by the STL containers). Also,
there is a difference whether an array class allocates objects with
placement new and later explicitly calls the destructor, and a class
which implements the copy assignment using this technique: Arrays, at
least in C++, contain object of identical types. Thus, the array class
knows what type of object to create. This does not hold for the copy
assignment since it can actually be a derived class (see also the
posting of Pete Becker).
--
<mailto:dietmar.kuehl@uni-konstanz.de>
<http://www.informatik.uni-konstanz.de/~kuehl/>
I am a realistic optimist - that's why I appear to be slightly pessimistic
---
[ 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: "Roger L. Cauvin" <rcauvin@homemail.com>
Date: 1997/03/25 Raw View
Herb Sutter <herbs@cntc.com> wrote in article <3334116b.98387323@herbs>...
> boukanov@sentef2.fi.uib.no (Igor Boukanov) wrote:
> >The better idea is to write,
> > foo::foo& operator=(const foo& xxx) {
> > this->~foo();
> > new (this) foo(xxx);
> > return *this;
> > }
> >
> >It seems this is more portable.
The idiom Igor proposes is frequently mentioned on C++ newsgroups. It is also
frequently criticized. Many of the criticisms are undeserved. The idiom has
its pitfalls, but works as long as certain guidelines are followed.
> Ignoring for now the nit that it won't deal with
> self-assignment:
Yes, Igor apparently forgot to code the check for self-assignment.
> The design is wrong because copy and assignment semantics are not the
> same.
Often the semantics of copy construction and copy assignment *are* the same.
A copy constructor should copy its argument, resulting in an object "equal" to
the argument. The corresponding assignment operator should, after discarding
the object's existing value, copy its argument. The assignment operator
should also result in an object "equal" to the argument.
> [T]his is inefficient at best....
The assignment idiom does involve some unnecessary overhead. The destructor
invoked by the assignment operator removes the object's vtbl pointer, and the
constructor re-inserts this same vtbl pointer. This overhead is minimal.
> [It is] [w]rong (because of slicing) at worst.
The assignment idiom will not result in slicing if the guidelines below are
followed:
1. If a class is to be used as a base class in a hierarchy, its assignment
operator(s) should be declared virtual.
2. Each of a class's assignment operators should have a corresponding
constructor.
3. Each derived class should, in addition to providing its own copy
assignment operator, override its base class assignment operator(s). These
assignment operators should be implemented in terms of corresponding
constructors.
> [P]refer writing a common private function to share code between
> copying and copy assignment, if necessary....
The practice of calling the same function from copy constructors and
assignment operators has its own pitfalls. The pitfalls exist because, during
execution of constructors, virtual function calls do not behave
polymorphically. During execution of assignment operators, however, virtual
functions calls *do* behave polymorphically. Code shared by a copy
constructor and an assignment operator may therefore behave inconsistently if
the code calls virtual functions.
Correct application of the destruct/placement new idiom to implement
assignment requires adherence to certain guidelines. Applying the idiom in
accordance with these guidelines has two advantages: it ensures the proper
semantic relationship between copy construction and assignment, and allows for
a mechanical implementation of assignment that is not prone to error (as long
as it is consistently applied). Aside from the observation that the idiom
performs unnecessary vtbl manipulation, most of its criticisms are undeserved.
---
Roger L. Cauvin
rcauvin@homemail.com
Software Engineer
National Instruments
---
[ 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: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/03/25 Raw View
"Risto Lankinen" <rlankine@us.oracle.com> writes:
|> Bob Sidebotham <rns@fore.com> wrote in article
|> <33330902.41C67EA6@fore.com>...
|> >
|> > I have a class like:
|> >
|> > class foo {
|> > bar& b;
|> > };
|> >
|> > and I'd like to be able to write an assignment operator for it.
|> > Unfortunately, the default assignment operator will not be generated,
|> > since it is defined as member wise assignment, and b is not assignable.
|>
|> Do the following two steps:
|>
0. Read very carefully the articles by Scott Meyers and Steve
Clamage as to why this is dangerous before deciding whether to
do it or not.
Unlike Scott and Steve, I think that there are certain specific cases
where it might be appropriate. But I would certainly condemn using it
without fully understanding the problems that Scott and Steve point out.
|> 1. Implement the assignment using explicit call to destructor and
|> copy constructor (to explicitly call copy constructor, use the
|> placement syntax of 'operator new()' ).
|> 2. Make sure the copy constructor reinitializes the reference the
|> way you want it to.
--
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: Pete Becker <pbecker@openenv.com>
Date: 1997/03/26 Raw View
Risto Lankinen wrote:
>
> Hi!
>
> Bob Sidebotham <rns@fore.com> wrote in article
> <33330902.41C67EA6@fore.com>...
> >
> > I have a class like:
> >
> > class foo {
> > bar& b;
> > };
> >
> > and I'd like to be able to write an assignment operator for it.
> > Unfortunately, the default assignment operator will not be generated,
> > since it is defined as member wise assignment, and b is not assignable.
>
> Do the following two steps:
>
> 1. Implement the assignment using explicit call to destructor and
> copy constructor (to explicitly call copy constructor, use the
> placement syntax of 'operator new()' ).
> 2. Make sure the copy constructor reinitializes the reference the
> way you want it to.
3. Document this technique clearly, so that anyone who attempts to
derive from this class will know that they cannot rely on the
compiler-generated assignment operator for their class to do the right
thing, nor can they write a "normal" assignment operator for their class
and expect it to do the right thing. Instead, they must also use this
technique in their classes. This is a tremendous burden to impose on
users of your class. It is not a good idea. Don't do it.
---
[ 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/03/26 Raw View
kuehl@uzwil.informatik.uni-konstanz.de (Dietmar Kuehl) writes:
|> Bob Sidebotham (rns@fore.com) wrote:
|> : Thanks for the replies on this one. Just to put this in perspective, I
|> : think there are legitimate reasons that one might want constant members
|> : or references and still be able to do structure assignment:
|>
|> Your reasons outlined below are *NOT* reasons why it might be
|> reasonable to do *assignment* to constant members or referernce members.
|> These are reasons why the objects should be constructed!
I'm not so sure. However, I do think that they are reasons which may
invalidate the usual objections to the destruct/copy construct idiom.
|> : 2. STL containers basically won't work with structures of this type,
|> : since they require an assignment operator to implement push_back.
|>
|> 'cont.push_back(x)' is defined as (that is, to have the effect of),
|> 'cont.insert(cont.end(), x)'. The 'insert()' method has to potentially
|> move the objects in the container and then construct a new element.
|> Although all implementations of STL I have available implement the
|> 'insert()' method to move elements using assignments instead of a
|> sequence of copy and destruction pairs, I don't think that this is
|> required. Thus, I don't think that STL by the definition as found in
|> the DWP makes it impossible to put elements of a type without an
|> assigment operator into a container like 'deque' or 'vector'.
|>
|> Also, I don't think that the implementation has to follow literally the
|> approach stated in the DWP: I think, the implementation is free to
|> implement 'push_back()' differently as long as the effect on the
|> container is the same (or is it really necessary that
|> 'cont.push_back(x)' calls 'cont.insert(cont.begin(), x)'?). For example
|> to the HP/SGI implementation of 'vector' this would mean, that some
|> checks can be avoided: If 'push_back()' is used, it is known in advance
|> that the new object is put at the end of the 'vector' and there is no
|> need to determine whether other objects have to be moved.
Whatever an actual implementation does is irrelevant. The standard says
that the objects in a container must support assignment, and the
implementation of the container is free to suppose that they do. (I
suppose that one could argue that the standard shouldn't say this.)
|> : 3. It's been suggested that I just use a pointer within the structure,
|> : rather than a reference. But there's legitimate reasons to want to
|> : record the fact that the reference is constant.
|>
|> Yes, it is legitimate to record the fact that the reference is
|> constant. But what you are asking for is to record the fact that a
|> reference is constant except sometimes, where it is not constant. Thus,
|> the recorded fact that it is constant is violated. I think, it is thus
|> unreasonable to record the fact that the reference is constant, since
|> this fact is false!
|>
|> If your problem is really that you want to have an array where objects
|> with constant member are to be stored and this is the reason why you
|> want to modify the constant members, then I strongly suggest that you
|> use an array class which constructs the elements in place. It is not
|> that hard to write, altough there are some tricky parts to it. This
|> would solve the problem in a clean and efficient way.
|>
|> Finally, a note on the use of placement new and explicit destruction:
|> Actually, the 'allocator' class of the standard defines the necessary
|> member to encapsulate both memory allocation and construction of
|> objects (and corresponding for the deallocation and destruction). Thus,
|> an array class should probably rather use an 'allocator' than direct
|> calls to placement new etc (this is done by the STL containers). Also,
|> there is a difference whether an array class allocates objects with
|> placement new and later explicitly calls the destructor, and a class
|> which implements the copy assignment using this technique: Arrays, at
|> least in C++, contain object of identical types. Thus, the array class
|> knows what type of object to create. This does not hold for the copy
|> assignment since it can actually be a derived class (see also the
|> posting of Pete Becker).
The key here is that he wants assignment uniquely to support use in an
array. The standard requires it for elements in e.g. vector.
The fact that the objects *are* in an array means that they are not
derived. Polymorphism doesn't work on objects in an array (at least in
C++). Thus, in this one special case, objections to the destruct/copy
construct idiom based on problems in the derived class don't hold.
For maximum safety, I would probably only use this implementation in a
type derived explicitly to be used as an array member, written in such a
manner as to prevent further derivation, e.g.:
// Suppose MyType the type in question. It contains a reference,
// and has no assignment operator. (The reference prevents the
// compiler from providing one.) It supports copy construction.
//
// For use in an array:
class MyTypeInArray : public MyType
, public virtual Underivable< MyTypeInArray >
{
public:
// The necessary constructors....
MyTypeInArray& operator=( MyTypeInArray const& rhs )
{
if ( this != &rhs )
{
this->~MyTypeInArray() ;
new ( this ) MyTypeInArray( rhs ) ;
}
return *this ;
}
} ;
The definition of Underivable is, of course:
template< class T >
class Underivable
{
friend class T ;
Underivable() {}
} ;
If the case occurs often enough, it might be worth creating a template
class for MyTypeInArray (say ArrayElement< T >); in this case, of
course, you will have to "fix" the constructors (probably just copy and
from T).
--
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
]