constructor and reset method

J

jason.cipriani

Thanks for the detailed response!
Well no, it would have to be

void reset() { T().swap( *this ); }

How is that different from "this->Swap(T())"?
However, there is a nice little elegant facility for iterating through members
in a little side project of Boost.Spirit, I forget the name (Poenix?), and
possibly that might serve as foundation for a general implementation?

Phoenix is here:

http://www.boost.org/libs/spirit/phoenix/index.html

From what I can tell skimming through the introduction now, it looks
like a project to somehow facilitate functional programming in C++. I
bet there's something good in there somewhere. I'll post back here if
I find a way to do it -- "Phoenix" looks like the kind of thing that
could distract me for hours so I'm going to have to put it off until
the weekend :) .
The swap idiom takes care of that, whereas other idioms may not necessarily take
care of that.

So... if I have:

void T::Swap (T &other) {
}

And 'other' is something derived from T... my Swap() would only swap
the members of 'other' that were in the T base, leaving any new
members in the derived class untouched. Is that behavior part of the
"swap idiom" (only swapping the members that two objects have in
common, leaving the others untouched) (it does seem reasonable)?
I don't see what the PIMPL idiom has to do with this, but possibly you mean
having a pointer to the real object state so that you only need to swap
pointers.

That is what I meant. Sorry for being unclear -- I was secretly still
looking at http://www.gotw.ca/gotw/059.htm and going off the rest of
the example there; where they did just that -- swapped pointers to the
object's state (and it's straight forward to update any state pointer
back to the object too; just as long as, like you mentioned above,
none of the members in the object state point back to the object
itself, which shouldn't happen unless you were sloppy).

Thanks for your time,
Jason
 
A

Alf P. Steinbach

* (e-mail address removed):
Thanks for the detailed response!


How is that different from "this->Swap(T())"?

If you just try it you'll see... ;-)

Phoenix is here:

http://www.boost.org/libs/spirit/phoenix/index.html

From what I can tell skimming through the introduction now, it looks
like a project to somehow facilitate functional programming in C++. I
bet there's something good in there somewhere. I'll post back here if
I find a way to do it -- "Phoenix" looks like the kind of thing that
could distract me for hours so I'm going to have to put it off until
the weekend :) .

I checked.

I think what I was thinking of was Fusion 2.0,

<url:
http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/fusion/extension/ext_full.html>.

Not sure how that can be simplified down, though.

So... if I have:

void T::Swap (T &other) {
}

And 'other' is something derived from T... my Swap() would only swap
the members of 'other' that were in the T base, leaving any new
members in the derived class untouched. Is that behavior part of the
"swap idiom" (only swapping the members that two objects have in
common, leaving the others untouched) (it does seem reasonable)?

Yeah, it's just usual C++ slicing.

However, for assignment nothing happens to the source object.

It's a (sliced or converted to) copy that's swapped.


Cheers, & hth.,

- Alf
 
J

James Kanze

(e-mail address removed)...
Is that something like this (fromhttp://www.gotw.ca/gotw/059.htm):
T& T::eek:perator=( const T& other )
{
T temp( other ); // do all the work off to the side
Swap( temp ); // then "commit" the work using
return *this; // nonthrowing operations only
}

That's the way I write it. Two other frequent alternatives are:

T&
T::eek:perator=(
T const& other )
{
T( other ).swap( *this ) ;
return *this ;
}

and

T&
T::eek:perator=(
T other )
{
swap( other ) ;
return *this ;
}

(Note that in the last case, the copy is actually done by the
caller, so you don't need it in the operator= function.)
And one advantage is there's really not much to do in the =
operator, since you are using the constructors to do a lot the
work?

The real advantage is that it is the simplest way to ensure that
all operations which can fail take place before you modify
anything in the object being assigned to.
So a Reset() might simply be:
void T::Reset () {
Swap(T());
}
But in that case... I mean, is there a way to implement Swap()
that doesn't involve explicitly typing out code to swap all of
the members?

Not really, since some of the members may have specialized swap
algorithms.
Since you just need to do a "shallow" swap... is something
like this reliable:
void T::Swap (T &other) {
char temp[sizeof(T)];
memcpy(temp, &other, sizeof(T));
memcpy(&other, *this, sizeof(T));
memcpy(*this, temp, sizeof(T));
}
No.

That seems like a horrible way to do it...

It is, since it involves undefined behavior.

Note that it's entirely possible that the swap idiom cannot be
used for certain classes---if there's no way to do a guaranteed
nothrow swap of some base class or member.
also, what if "other" is something derived from T?

What about it? Assignment cannot change the type of the object
being assigned, so you only assign the T part of the derived
object. (In general, assignment and polymorphism don't work
well together, and if a class is designed to be used as a base
class, it's usually best to inhibit assignment in it.)
Is there a way to just use the compilers default assignment
operator even when you've defined your own?

No. Presumably, if you provide your own assignment operator,
it's because the compiler's default assignment operator doesn't
do what you want.
Or does it always make sense to use the "pimpl" idiom when
implementing a swap function, so that you don't have to do
something like that?

In a certain sense, it always makes sense to use the swap idiom
for assignment if you use the compilation firewall idiom.
Except that in the case of the compilation firewall, you're
dealing with an easily recognizable idiom, and even the standard
implementation of operator= is so simple that I don't find it
worth the bother. Generally speaking:

-- if the class corresponds to an established idiom (like the
compilation firewall), then use the assignment operator for
the idiom, otherwise,

-- if all members support value nothrow value assignment, then
the compiler defined assignment operator is all you need; no
point in the swap idiom, otherwise,

-- if all of the members support value assignment, and you
don't need the strong exception guarantee, then the compiler
defined assignment operator is all you need as well,
otherwise,

-- if all of the members are either non-class types, or class
types which support a nothrow swap (generally, which have a
member function swap), then use the swap idiom, using
std::swap for the non-class types, and the nothrow swap for
the class types, otherwise

-- you're on your own, and it may be difficult.

In practice, if all of the libraries you're using understand the
swap idiom, and have been written in consequence, the fourth
case will be rather frequent. If you have to deal with legacy
libraries, however, you may find that you fall into the fifth
case more often than you'd like. In such cases, I find that it
often is convenient to use the compilation firewall idiom in
such cases, to move them up to the first case above.
 
J

James Kanze

Thanks for the detailed response!
How is that different from "this->Swap(T())"?

It's legal code. It's hard to imagine swap taking anything but
a non-const reference as an argument, and of course, you can't
initialize a non-const reference with a temporary (even if some
compilers fail to detect the error).

[...]
(Not knowing what the problem is, I can't say whether swap takes
care of it or not. I don't see how swap has any impact one way
or the other, but perhaps Alf sees a problem that I don't.)
So... if I have:
void T::Swap (T &other) {
}
And 'other' is something derived from T... my Swap() would
only swap the members of 'other' that were in the T base,
leaving any new members in the derived class untouched.

How could it do otherwise, given that it doesn't know anything
about the derived class?

But of course, that question is moot in the swap idiom for
assignment, since other will never be an object with a derived
class type.
Is that behavior part of the "swap idiom" (only swapping the
members that two objects have in common, leaving the others
untouched) (it does seem reasonable)?

It doesn't seem reasonable to me to support assignment if the
class is to be used as a base class.
That is what I meant. Sorry for being unclear -- I was
secretly still looking athttp://www.gotw.ca/gotw/059.htmand
going off the rest of the example there; where they did just
that -- swapped pointers to the object's state (and it's
straight forward to update any state pointer back to the
object too; just as long as, like you mentioned above, none of
the members in the object state point back to the object
itself, which shouldn't happen unless you were sloppy).

In the case of the compilation firewall idiom, you can get even
simpler than swap. The standard operator= in that case is:

T&
T::eek:perator=( T const* other )
{
Impl* tmp = new Impl( *other.myImpl ) ;
// or = other.myImpl->clone()...
delete myImpl ;
my Impl = tmp ;
return *this ;
}

The swap idiom is also quite acceptable in this case, but
perhaps for historical reasons, I find the above more idiomatic.
 
J

James Kanze

* (e-mail address removed):
If you just try it you'll see... ;-)

Maybe, maybe not. I think that there are still some widespread
compilers which let it through, without so much as a warning
(although it has been illegal now for almost 20 years).
 
J

James Kanze

* James Kanze:
Well, I was thinking of the above as a pattern, used also in derived classes.
Then a call chain :)
Derived::Derived
-> Base::Base
-> Base::Base[init-list] // Base members def-inits
-> Base::Base[body]
-> Base::eek:perator=() // Base members assigned
-> Derived::Derived[init-list] // Derived members def-inits.
-> Derived::Derived[body]
-> Derived::eek:perator=()
-> Base::eek:perator=() // Base members assigned 2nd time.
-> [assignment statements] // Derived members assigned

Good point. Arguable, if your class supports assignment, then
assigning the same value twice shouldn't cause any problems.
But it's certainly something I'd prefer avoiding, just in case.
(Of course, arguably, if your class is being used as a base
class, it shouldn't support assignment at all. But that's
another issue.)
[snip]
In simple cases, you may be able to do even simpler than the
swap idiom

You wouldn't really use the swap idiom for something like
complex, would you? (For that matter, would you ever use it if
the compiler generated assignment operator would work?)

Even in the case of the compilation firewall idiom, I tend to
just use the classical assignment operator---probably because I
was using the compilation firewall idiom long before I'd ever
heard of the swap idiom. (It was called the Cheshire cat idiom
back then---the implementation just disappears, until all you
see is its smile, and then not even that.) It's in my fingers.
But if I were to change, I'd use a boost::scoped_ptr, and
simply:

T&
T::eek:perator=( T const& other )
{
myImpl.reset( new Impl( *other.myImpl ) ) ;
return *this ;
}

You could use the swap idiom, but it seems more complex here,
and it is certainly not necessary.

Note that in the case of the compilation firewall idiom, part of
the reason why the swap idiom isn't necessary is because the
compilation firewall idiom is an established pattern. I don't
have to think about things like exception safety issues, because
the "standard" solutions are known to be exception safe. In a
more general case, I might adopt the swap idiom simply because
it means that I know I have exception safety, without having to
think any further about it.

Not that I don't like thinking, but there are always enough
other things to think about, for which there are no simple
solutions, to keep me occupied. Anytime there's a simple
solution which is guaranteed to work, I'm all for it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,763
Messages
2,569,562
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top