C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

self-assignment and unrelated pointers

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Slawomir Lisznianski
Guest





PostPosted: Sun Nov 13, 2005 10:53 pm    Post subject: self-assignment and unrelated pointers Reply with quote



Hello,

For self-assignment check, various sources (Meyers, C++ FAQ) suggest
something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

Wouldn't the `==' expresion yield unspecified result in case two
pointers were "unrelated" (5.9.2)?

If so, shouldn't the self-assignmet check be implemented using
std::less, for example:

Fred & Fred::operator=(Fred const & f)
{
if (!std::less<void*>()(this, &f) &&
!std::less<void*>()(&f, this))
{
return *this;
}

// Put the normal assignment duties here...

return *this;
}

Cheers,

--
Slawomir Lisznianski
Paramay Group, LLC
"Programs for Research Machinery"

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Greg Herlihy
Guest





PostPosted: Mon Nov 14, 2005 9:23 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote



Slawomir Lisznianski wrote:
Quote:
Hello,

For self-assignment check, various sources (Meyers, C++ FAQ) suggest
something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

Wouldn't the `==' expresion yield unspecified result in case two
pointers were "unrelated" (5.9.2)?


No, pointers of the same type can always be compared for equality. See
§5.10/1.


Quote:
If so, shouldn't the self-assignmet check be implemented using
std::less, for example:

Fred & Fred::operator=(Fred const & f)
{
if (!std::less<void*>()(this, &f) &&
!std::less<void*>()(&f, this))
{
return *this;
}

// Put the normal assignment duties here...

return *this;
}

This version is no more correct than the original one, but is far more
cluttered. Furthermore, the embellishments have done little to place
C++ as a programming language in a flattering - or really even a fair -
light.

Presenting this version as optimal or "textbook" C++ code would serve
only to perpetuate the notion that C++ is marred by a baroque syntax in
which even a simple test for equality (which in most languages is a ==
or a =) must instead be buried in an incomprehensible sea of <, >, :,
*. ), (. and &'s.

So at this point, I would say that my preference is leaning toward
keeping the original version.

Greg


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Carl Barron
Guest





PostPosted: Mon Nov 14, 2005 9:23 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote



Slawomir Lisznianski <newsgroup (AT) paramay (DOT) com> wrote:

Quote:
Hello,

For self-assignment check, various sources (Meyers, C++ FAQ) suggest
something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

Wouldn't the `==' expresion yield unspecified result in case two
pointers were "unrelated" (5.9.2)?


The condition checks that the two Fred *'s point to the same

location. The if is testing two raw Fred *'s and that is that
the addresses are the same. I might write it as
if(this != &f)
{
//...
}
return *this;
but most modern compilers will generate the same code...

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Carlos Moreno
Guest





PostPosted: Mon Nov 14, 2005 9:25 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Slawomir Lisznianski wrote:
Quote:
Hello,

For self-assignment check, various sources (Meyers, C++ FAQ) suggest
something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

Wouldn't the `==' expresion yield unspecified result in case two
pointers were "unrelated" (5.9.2)?

If so, shouldn't the self-assignmet check be implemented using
std::less, for example:

Fred & Fred::operator=(Fred const & f)
{
if (!std::less<void*>()(this, &f) &&
!std::less<void*>()(&f, this))
{
return *this;
}

// Put the normal assignment duties here...

return *this;
}

Actually, *all* of the above is "wrong" :-)

This is how you implement an assignment operator:

Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}

It relies on Fred's copy constructor and on a Fred::swap
member function that presumably does the swap efficiently
and without possibility of throwing an exception (for
instance, swapping member-by-member, including pointers --
just swap the values of the pointers).

There is no reason why you should ever need to check
against self-assignment -- you implement it the right way
and everyone will be happy. (well.... Smile)

But I can't help but be curious... What do you think is
wrong with the fragment of code you posted? (putting aside
the fact that it's not exception-safe). What do you mean
by "unrelated" pointers? Pointers to different data types?
this and &f are guaranteed to be pointers to the same data
type -- if you're hoping that the assignment will be feasable,
you'd better rely on the pointers to be "non-unrelated".

HTH,

Carlos
--

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Jack Klein
Guest





PostPosted: Mon Nov 14, 2005 9:36 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

On 13 Nov 2005 17:53:49 -0500, Slawomir Lisznianski
<newsgroup (AT) paramay (DOT) com> wrote in comp.lang.c++.moderated:

Quote:
Hello,

For self-assignment check, various sources (Meyers, C++ FAQ) suggest
something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

Wouldn't the `==' expresion yield unspecified result in case two
pointers were "unrelated" (5.9.2)?

No, two pointers of the same type can always be tested for equality or
inequality with defined results. See 5.10.1. The requirements are
less stringent for equality testing than they are for relational
testing.

The test above is well defined.

Quote:
If so, shouldn't the self-assignmet check be implemented using
std::less, for example:

Fred & Fred::operator=(Fred const & f)
{
if (!std::less<void*>()(this, &f) &&
!std::less<void*>()(&f, this))
{
return *this;
}

// Put the normal assignment duties here...

return *this;
}

Cheers,

std::less is a relational test, and in fact is subject to the same
limitations as coding the relational operators directly. Not only
that, but pointers to void are not exempt from these requirements, and
in fact may have more issues, since relational comparisons at least
imply arithmetic between the two pointers, and it is not possible to
perform pointer arithmetic on pointers to void.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Daniel Krügler
Guest





PostPosted: Mon Nov 14, 2005 9:36 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Slawomir Lisznianski wrote:
Quote:
Hello,

For self-assignment check, various sources (Meyers, C++ FAQ) suggest
something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

Wouldn't the `==' expresion yield unspecified result in case two
pointers were "unrelated" (5.9.2)?

No. The proper quote would be 5.9.10, because we have an equality test
here, no relational operators. Note that 5.9.10/p.1 allows this:

"[..] Pointers to objects or functions of the same type (after pointer
conversions) can be compared for equality. Two pointers of the same type
compare equal if and only if they are both null, both point to the same
function, or both represent the same address (3.9.2)."

Greetings from Bremen,

Daniel Krügler

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
kanze
Guest





PostPosted: Mon Nov 14, 2005 2:35 pm    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Carlos Moreno wrote:
Quote:
Slawomir Lisznianski wrote:

For self-assignment check, various sources (Meyers, C++ FAQ)
suggest something along the lines of:

Fred & Fred::operator=(Fred const & f)
{
if (this == &f) return *this;
^^^^^^^^^^
// Put the normal assignment duties here...

return *this;
}

[...]
Quote:
Actually, *all* of the above is "wrong" Smile

Without knowing something of what "normal assignment duties"
are, I wouldn't go that far.

Quote:
This is how you implement an assignment operator:

Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}

That's one way, which can be used to give the strong guarantee
in certain specific cases. It's certainly not a universal
solution, since...

Quote:
It relies on Fred's copy constructor and on a Fred::swap
member function that presumably does the swap efficiently and
without possibility of throwing an exception (for instance,
swapping member-by-member, including pointers -- just swap the
values of the pointers).

That's presuming a lot. *If* the presumptions are true, *then*
the swap idiom is a good solution. There are, however, lots of
cases where it isn't appropriate. You surely wouldn't write the
assignment operator for complex this way, would you? Or Point3D
(which consists of 3 ints)? (To begin with, complex and Point3D
probably don't have a member function swap.)

Quote:
There is no reason why you should ever need to check against
self-assignment -- you implement it the right way and everyone
will be happy. (well.... Smile)

There is a fairly strong presumption that if you need to check
for self assignment, the assignment operator probably doesn't
offer the strong guarantee with regards to exception handling;
often, in fact, it will not even be exception safe at all. But
there are many, many cases where self assignment poses no
problem, and there is no reason to use the swap idiom in such
cases. In fact, if assignment is correctly defined for all
member elements, and you don't need the strong guarantee, just
assigning the elements, one after the other, is the simplest
solution.

If I look at my own code, I find that the swap idiom is fairly
widespread in my low-level library code, where classes manage
their own memory (collections, etc.). It's much, much rarer at
the application level, where classes typically only contain
objects for which assignment is already well defined. In fact,
in almost every case, the only reason the assignment operator is
declared is either to make it private, or to prevent it from
being inline. (The swap idiom will be used if the strong
guarantee is needed and the assignment operator of any of the
members can throw. But the first condition isn't that frequent.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
kanze
Guest





PostPosted: Mon Nov 14, 2005 2:37 pm    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Jack Klein wrote:

[...]
Quote:
std::less is a relational test, and in fact is subject to the
same limitations as coding the relational operators directly.

That's not true. §20.3.3/8 specifically says "For templates
greater, less, greater_equal, and less_equal, the
specializations for any pointer type yield a total order, even
if the built-in operators <, >, <=, >= do not." If '<' really
doesn't work for all built-in pointers, the implementation is
required to partially specialize std::less to provide something
that does.

Quote:
Not only that, but pointers to void are not exempt from these
requirements, and in fact may have more issues, since
relational comparisons at least imply arithmetic between the
two pointers, and it is not possible to perform pointer
arithmetic on pointers to void.

I don't see where the relational comparisons imply any
arithmetic. And I don't see any restrictions concerning void*;
std::less as well, and has defined results in the same conditions in which
other types of pointers have defined results.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Ron Natalie
Guest





PostPosted: Mon Nov 14, 2005 5:20 pm    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Carlos Moreno wrote:

Quote:
Actually, *all* of the above is "wrong" :-)

This is how you implement an assignment operator:

Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}

This may or may not be optimal, but is a useful idiom

for many situations. Of course if you neglect to define
Fred::swap, you'll likely recurse infinitely.

Quote:
It relies on Fred's copy constructor and on a Fred::swap
member function that presumably does the swap efficiently
and without possibility of throwing an exception (for
instance, swapping member-by-member, including pointers --
just swap the values of the pointers).

Actually, the swap can even throw exceptions provided that
it behaves well in light of them (i.e., leaves neither tmp
nor Fred in an state that will cause them to not destruct
properly).

Quote:

There is no reason why you should ever need to check
against self-assignment -- you implement it the right way
and everyone will be happy. (well.... Smile)

That is true, although if self-assignment is a likely
operation and the safe assignment idiom is costly, you
might want to short circuit it. However, this is a
pretty far reach.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Ron Natalie
Guest





PostPosted: Tue Nov 15, 2005 7:51 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Ron Natalie wrote:

Quote:
Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}

This may or may not be optimal, but is a useful idiom
for many situations. Of course if you neglect to define
Fred::swap, you'll likely recurse infinitely.

Oops, ignore my statement on recursion, it won't compile

if Fred::swap isn't defined I was thinking about using
std::swap.

Yet a better solution is to design your classes to
NOT REQUIRE special copying semantics. Most of the
time you can use a better paradigm (strings, vectors,
other debugged pointer wrappers) that obviates
the need for you to define operator=/copy constructor/
destruction semantics.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Carlos Moreno
Guest





PostPosted: Tue Nov 15, 2005 7:52 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Ron Natalie wrote:
Quote:
Carlos Moreno wrote:


Actually, *all* of the above is "wrong" :-)

This is how you implement an assignment operator:

Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}


This may or may not be optimal, but is a useful idiom
for many situations. Of course if you neglect to define
Fred::swap, you'll likely recurse infinitely.

Huh??

If Fred::swap is missing, the above would fail to compile...
I'm not sure what you had in mind when talking about the
possible infinite recursion...

Quote:
It relies on Fred's copy constructor and on a Fred::swap
member function that presumably does the swap efficiently
and without possibility of throwing an exception (for
instance, swapping member-by-member, including pointers --
just swap the values of the pointers).

Actually, the swap can even throw exceptions provided that
it behaves well in light of them

Sure. But it should be always possible (and most likely
desireable) to provide a class::swap that does not throw,
provided that the data members' types provide a no-throwing
swap member function, or that they are built-in types.

For built-in types, swap (std::swap, that is) is guaranteed
not to throw; for UDT types, apply the above reasoning
recursively down the data hierarchy, with the recursion
"base case" being the members that are built-in data types.

For instance:

class Person
{
string firstname, lastname;
vector<string> phone_numbers;
list< vector friends_phone_numbers;
char gender;
bool married;

public:
// ...
};

void Person::swap (Person & other)
{
firstname.swap (other.firstname); // internally swaps pointers
lastname.swap (other.lastname);
phone_numbers.swap (phone_numbers); // same as above
friends... .swap (friends ...); // same

std::swap (gender, other.gender);
std::swap (married, other.married);
std::swap (ptr_member, other.ptr_member);
}

None of the statements can throw an exception, since
they are, or they boil down to, assignments of built-in
data types.

Quote:
That is true, although if self-assignment is a likely
operation and the safe assignment idiom is costly, you
might want to short circuit it. However, this is a
pretty far reach.

Actually, the create-a-temporary-then-swap idiom for the
assignment operator *can* have a check for self-assignment:

const Person & Person::operator= (const Person & other)
{
if (this != &other)
{
Person tmp(other);
other.swap (*this); // or just swap(other);
}

return *this;
}

And it just acts as an optimization -- the idiom does not
*rely* on the check, nor does the check make it exception-
unsafe.

Carlos
--

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Carlos Moreno
Guest





PostPosted: Tue Nov 15, 2005 9:21 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

kanze wrote:

Quote:
Actually, *all* of the above is "wrong" :-)

Without knowing something of what "normal assignment duties"
are, I wouldn't go that far.

Well, it always depends on the point of view... As you
mentioned, regardless of what the "normal assignment duties"
does, assuming that an assignment operator is necessary for
the class, then it would be impossible to make it exception-
safe (well, at least it is generally accepted that it is
the case, even if it has not been mathematically proven)

Quote:
This is how you implement an assignment operator:

Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}


That's one way, which can be used to give the strong guarantee
in certain specific cases. It's certainly not a universal
solution, since...


It relies on Fred's copy constructor and on a Fred::swap
member function that presumably does the swap efficiently and
without possibility of throwing an exception (for instance,
swapping member-by-member, including pointers -- just swap the
values of the pointers).


That's presuming a lot. *If* the presumptions are true, *then*
the swap idiom is a good solution. There are, however, lots of
cases where it isn't appropriate. You surely wouldn't write the
assignment operator for complex this way, would you?

This is not a valid argument -- you simply would not write an
overloaded assignment operator for such class; *if* we assume
that you are writing it because one is *required* (as is the
case with classes that have members that are pointers to
dynamically allocated memory that is owned by the object),
then presuming that you have a copy-ctor is definitely not too
much. Presuming that you have a swap member is not *that* much
either. It makes sense that it is there to provide a clean and
efficient mechanism for swapping values, and it certainly pays
off just for the purpose of writing a "cleaner" version of the
assignment operator.

BTW, even if you don't have a swap member function, nothing
prevents you from using that idiom:

Person tmp (other);
std::swap (member1, other.member1);
std::swap (member2, other.member2);
member3_vec.swap (other.member3_vec);
return *this;

(in the example, member3_vec is a vector<something>, so we do
rely on its swap member function, which is efficient and does
not throw exceptions -- actually, now that I think about it,
does the standard guarantee this? Or is it simply a matter
of QOI, or rather, a matter of it's always like that because
vector::swap is always implemented as a few swaps, including
swapping pointers?)

Quote:
In fact, if assignment is correctly defined for all
member elements, and you don't need the strong guarantee, just
assigning the elements, one after the other, is the simplest
solution.

But again, in that case we wouldn't be writing an assignment
operator (not the way you describe it, since that is what the
compiler would do).

Quote:
If I look at my own code, I find that the swap idiom is fairly
widespread in my low-level library code, where classes manage
their own memory (collections, etc.).

Exactly.

Quote:
(The swap idiom will be used if the strong
guarantee is needed and the assignment operator of any of the
members can throw. But the first condition isn't that frequent.)

Ok. But I also see the swap idiom a way of writing clean code;
writing the copy ctor and the assignment operator the "classical"
way involves redundant coding. Implementing one in terms of the
other one certainly tends to reduce the debgging time (and the
total amount and intensity of the headaches Smile)

I can concede that yes, perhaps my original blanket statement
that "*this* is how you implement an assign. op" was perhaps a
bit too strong... I'd probably amend it by saying that I find
that the swap idiom is a solution that most of the time is
at least as good as any other solution in every respect, and
superior in some respects, so it makes sense that one adopts
it as the "default" way to implement op=. Only when there are
strong reasons not to, one would consider other approaches
(one reason would be non-abundant memory and the lack of a
strong guarantee requirement -- the swap idiom can fail to
allocate in situations where the other one wouldn't, since
it has to hold *both* objects simultaneously)

Carlos
--

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Jack Klein
Guest





PostPosted: Tue Nov 15, 2005 9:40 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

On 14 Nov 2005 09:37:08 -0500, "kanze" <kanze (AT) gabi-soft (DOT) fr> wrote in
comp.lang.c++.moderated:

Quote:
Jack Klein wrote:

[...]
std::less is a relational test, and in fact is subject to the
same limitations as coding the relational operators directly.

That's not true. §20.3.3/8 specifically says "For templates
greater, less, greater_equal, and less_equal, the
specializations for any pointer type yield a total order, even
if the built-in operators <, >, <=, >= do not." If '<' really
doesn't work for all built-in pointers, the implementation is
required to partially specialize std::less to provide something
that does.

Thanks for that correction, I had a hunch that I might be missing
something, but I recklessly plunged ahead regardless.

Quote:
Not only that, but pointers to void are not exempt from these
requirements, and in fact may have more issues, since
relational comparisons at least imply arithmetic between the
two pointers, and it is not possible to perform pointer
arithmetic on pointers to void.

I don't see where the relational comparisons imply any
arithmetic. And I don't see any restrictions concerning void*;
std::less as well, and has defined results in the same conditions in which
other types of pointers have defined results.

I agree with this, given what you have shown about templates, for
std::less.

I was going to argue about '<' with pointers to void, but I've already
done my reckless plunging for the week. You are absolutely correct,
C++ allows it and, even more to my surprise, C allows relational
comparisons between "compatible incomplete types".

Learn something new every day.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
kanze
Guest





PostPosted: Tue Nov 15, 2005 2:36 pm    Post subject: Re: self-assignment and unrelated pointers Reply with quote

Carlos Moreno wrote:
Quote:
kanze wrote:

Actually, *all* of the above is "wrong" :-)

Without knowing something of what "normal assignment duties"
are, I wouldn't go that far.

Well, it always depends on the point of view... As you
mentioned, regardless of what the "normal assignment duties"
does, assuming that an assignment operator is necessary for
the class, then it would be impossible to make it exception-
safe (well, at least it is generally accepted that it is the
case, even if it has not been mathematically proven)

What do you mean, impssible to make it exception-safe? It's
usuallly pretty simple.

What is probably true is that if a test for self assignment is
necessary, the implementation probably isn't exception safe. I
suspect that there are exceptions, but my comment does refer
more to what followed in your posting. There are many correct
solutions to writing assignment operators.

Quote:
This is how you implement an assignment operator:

Fred & Fred::operator= (const Fred & f)
{
Fred tmp (f);
tmp.swap (*this);
return *this;
}

That's one way, which can be used to give the strong
guarantee in certain specific cases. It's certainly not a
universal solution, since...

It relies on Fred's copy constructor and on a Fred::swap
member function that presumably does the swap efficiently
and without possibility of throwing an exception (for
instance, swapping member-by-member, including pointers --
just swap the values of the pointers).

That's presuming a lot. *If* the presumptions are true,
*then* the swap idiom is a good solution. There are,
however, lots of cases where it isn't appropriate. You
surely wouldn't write the assignment operator for complex
this way, would you?

This is not a valid argument -- you simply would not write an
overloaded assignment operator for such class;

Sure you would. You don't want the assignment operator to be
inline, do you? (Often, of course, you don't want it to be
public, but then, you generally don't worry about an
implementation.)

Quote:
*if* we assume that you are writing it because one is
*required* (as is the case with classes that have members
that are pointers to dynamically allocated memory that is
owned by the object), then presuming that you have a copy-ctor
is definitely not too much.

I would presume that any class supporting assignment also
supported copy. The language doesn't impose it, but good design
does. (The reverse is not always true, of course.)

Quote:
Presuming that you have a swap member is not *that* much
either.

It is and it isn't. It depends a lot on what the class is
doing.

Quote:
It makes sense that it is there to provide a clean and
efficient mechanism for swapping values, and it certainly pays
off just for the purpose of writing a "cleaner" version of the
assignment operator.

As I said in my posting, I use the idiom a lot in low level
classes, and very rarely in application level ones.

Quote:
BTW, even if you don't have a swap member function, nothing
prevents you from using that idiom:

Person tmp (other);
std::swap (member1, other.member1);
std::swap (member2, other.member2);
member3_vec.swap (other.member3_vec);
return *this;

EXcept, of course, the fact that std::swap may throw. One
potential problem is that the swap idiom only works if all of
the classes used in the implementation also use it. In
practice, I've found that between legacy code and third party
software, there are a lot of cases where it simply doesn't work.
So while I try to design new low level classes so that it can be
used by there client classes, by providing a no throw swap
member function, at the application level, I have an absolute
rule that it may not be used unless every member of class type
provides a swap function -- std::swap may only be used on basic
types and pointers. This is overly restrictive, since std::swap
also works if the class type has a non throwing copy constructor
and assignment operator, but it's a lot easier to enforce.
(Note that the fact that the copy constructor's and the
assignment operator's code doesn't throw isn't sufficient. The
copy constructor and assignment operator must be documented as
no throw, as part of its contract. Now take a look at the third
party software you use, and see how often this is the case.)

Quote:
(in the example, member3_vec is a vector<something>, so we do
rely on its swap member function, which is efficient and does
not throw exceptions -- actually, now that I think about it,
does the standard guarantee this?

I would have thought so, but I can't find it.

Quote:
Or is it simply a matter of QOI, or rather, a matter of it's
always like that because vector::swap is always implemented as
a few swaps, including swapping pointers?)

Well, swap is required to have constant complexity, which means
that it cannot copy contained elements. Which makes it hard to
conceive of an implementation which throws.

Quote:
In fact, if assignment is correctly defined for all member
elements, and you don't need the strong guarantee, just
assigning the elements, one after the other, is the simplest
solution.

But again, in that case we wouldn't be writing an assignment
operator (not the way you describe it, since that is what the
compiler would do).

Yes, but the compiler's version is inline, which is generally
something you want to avoid.

Note that if the class uses the compilation firewall idiom, you
still have to provide a user defined assignment operator, even
if there's no reason to use the swap idiom:

MyClass&
MyClass::operator=( MyClass const& other )
{
myImpl = other.myImpl->clone() ;
return *this ;
}

Even without a smart pointer or garbage collection:

MyClass&
MyClass::operator=( MyClass const& other )
{
Impl* tmp = other.myImpl->clone() ;
delete myImpl ;
myImpl = tmp ;
return *this ;
}

does the job very well. (Normally, of course, the Boehm
collector takes care of things. Or in the absense of the Boehm
collector, boost::scoped_ptr. And the first version is
sufficient.)

Quote:
If I look at my own code, I find that the swap idiom is
fairly widespread in my low-level library code, where
classes manage their own memory (collections, etc.).

Exactly.

(The swap idiom will be used if the strong guarantee is
needed and the assignment operator of any of the members can
throw. But the first condition isn't that frequent.)

Ok. But I also see the swap idiom a way of writing clean
code; writing the copy ctor and the assignment operator the
"classical" way involves redundant coding. Implementing one
in terms of the other one certainly tends to reduce the
debgging time (and the total amount and intensity of the
headaches Smile)

Again, I think that depends on the class. I tend to use it when
there is significant work involved, for that reason. But it's
not something to be religious about. When it works, use it, but
don't force things just to use it.

Quote:
I can concede that yes, perhaps my original blanket statement
that "*this* is how you implement an assign. op" was perhaps a
bit too strong... I'd probably amend it by saying that I find
that the swap idiom is a solution that most of the time is at
least as good as any other solution in every respect, and
superior in some respects, so it makes sense that one adopts
it as the "default" way to implement op=.

I'd say that the "default" way is still member-wise assignment.
What the compiler does, more or less, but not inline.
(Actually, the default way is to define the operator private,
and not implement it at all. The code you don't write generally
has the least errors. But there are a lot of cases where not
supporting assignment isn't acceptable.) I'd go further and say
that this also holds for assignment-lik actions, things like "p
= other.p->clone()". Specific idioms, like the compilation
firewall or the letter/envelope, often have specific "defaults",
which you'll want to use, rather than the swap idiom.

Quote:
Only when there are strong reasons not to, one would consider
other approaches (one reason would be non-abundant memory and
the lack of a strong guarantee requirement -- the swap idiom
can fail to allocate in situations where the other one
wouldn't, since it has to hold *both* objects simultaneously)

I tend to take the opposite view. In the absense of a strong
guarantee requirement, or a requirement that the class be usable
in the implementation of other classes, some of which may need
the strong guarantee, the swap idiom often doesn't even come
into consideration.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
John Potter
Guest





PostPosted: Wed Nov 16, 2005 12:45 am    Post subject: Re: self-assignment and unrelated pointers Reply with quote

On 15 Nov 2005 09:36:31 -0500, "kanze" <kanze (AT) gabi-soft (DOT) fr> wrote:

Quote:
Carlos Moreno wrote:

(in the example, member3_vec is a vector<something>, so we do
rely on its swap member function, which is efficient and does
not throw exceptions -- actually, now that I think about it,
does the standard guarantee this?

I would have thought so, but I can't find it.

23.1/10.

Quote:
Or is it simply a matter of QOI, or rather, a matter of it's
always like that because vector::swap is always implemented as
a few swaps, including swapping pointers?)

Well, swap is required to have constant complexity, which means
that it cannot copy contained elements. Which makes it hard to
conceive of an implementation which throws.

The authors of the standard did think of compare objects in associative
containers, which would be swapped by assignment not swap, throwing. I
agree that it is hard to conceive of a compare object with a throwing
copy constructor or assignment operator.

John

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.